-----BEGIN PGP SIGNATURE-----
iQIcBAABAgAGBQJYkOZYAAoJENro4Ql1lpzlrSAQAJXut/KGZDCKoalPEaksmtLT wrj8y/c/B7C61wIAxBKbWGaDuNCOzvJiTSLbdPCs+WF295/QzIh0tcxnwMBw1+RY F74UqFtX7oo8QolAbAI/MjiPYsgFk3HPtsxF5Wn82RskogSZPHlTVb8SqBasngm6 iTGsXn8LI6FvFQKtUcv4yWxSfmJyQhv+H1hOg7+nFLoXnygvhW2y4NRHFH/atLHq +6bxPxxuo/sIqMvB5RRAnO03o4saXV3+ZqGRX1iOm27waxLK/NgR3N/0W3kEcQJV I0ExIFHJ3qMas2kIF85ii5q1F55ey4VfzRrExl3t8Bd2CP7oTXhn4t69z/7cwwtL K0Un6fHM7h7zzCeyXwhQ5RxHuYgcJcR5LLQfIJmz04CXr8Bk7G0wKjJAcWt8Ijim IiGs1U+Lp1HvoVFAztEgQNdXdmMektngFTYv8efQBqWP6Od3pDGzOla7cqN1uve+ F6p+gzLWWdOWHwfWX75iTgQPtdaoYPieu8AHxnFECn5Q+xTZ0sKv7RcQWuIDcOin UIhzUHsTNyZLLDOpxZ8LzFpXBjTRvsOnoLCvG62e4K/+mL79yOJPAi/i5hdgLZMm goOnJk76G21oJuFmHIEG5+fcdoCb8/cwci0Tiw/1rJgK3/Fu20136zY7oCZGS1bY 3OjDqzdp9cxHAdI1JmcT =YkDp -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/elmarco/tags/chr-split-pull-request' into staging # gpg: Signature made Tue 31 Jan 2017 19:32:40 GMT # gpg: using RSA key 0xDAE8E10975969CE5 # gpg: Good signature from "Marc-André Lureau <marcandre.lureau@redhat.com>" # gpg: aka "Marc-André Lureau <marcandre.lureau@gmail.com>" # gpg: WARNING: This key is not certified with sufficiently trusted signatures! # gpg: It is not certain that the signature belongs to the owner. # Primary key fingerprint: 87A9 BD93 3F87 C606 D276 F62D DAE8 E109 7596 9CE5 * remotes/elmarco/tags/chr-split-pull-request: (41 commits) char: headers clean-up char: move parallel chardev in its own file char: move serial chardev to its own file char: move pty chardev in its own file char: move pipe chardev in its own file char: move console in its own file char: move stdio in its own file char: move file chardev in its own file char: move udp chardev in its own file char: move socket chardev to its own file char: move win-stdio into its own file char: move win chardev base class in its own file char: move fd chardev in its own file char: move QIOChannel-related stuff to char-io.h char: remove unused READ_RETRIES char: rename and move to header CHR_READ_BUF_LEN char: move ringbuf/memory to its own file char: move mux to its own file char: move null chardev to its own file char: make null_chr_write() the default method ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
2d6752d38d
@ -1194,8 +1194,9 @@ T: git git://github.com/jnsnow/qemu.git bitmaps
|
||||
|
||||
Character device backends
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
M: Marc-André Lureau <marcandre.lureau@redhat.com>
|
||||
S: Maintained
|
||||
F: qemu-char.c
|
||||
F: chardev/
|
||||
F: backends/msmouse.c
|
||||
F: backends/testdev.c
|
||||
|
||||
|
4
Makefile
4
Makefile
@ -147,6 +147,7 @@ endif
|
||||
|
||||
dummy := $(call unnest-vars,, \
|
||||
stub-obj-y \
|
||||
chardev-obj-y \
|
||||
util-obj-y \
|
||||
qga-obj-y \
|
||||
ivshmem-client-obj-y \
|
||||
@ -223,7 +224,8 @@ subdir-dtc:dtc/libfdt dtc/tests
|
||||
dtc/%:
|
||||
mkdir -p $@
|
||||
|
||||
$(SUBDIR_RULES): libqemuutil.a libqemustub.a $(common-obj-y) $(qom-obj-y) $(crypto-aes-obj-$(CONFIG_USER_ONLY))
|
||||
$(SUBDIR_RULES): libqemuutil.a libqemustub.a $(common-obj-y) $(chardev-obj-y) \
|
||||
$(qom-obj-y) $(crypto-aes-obj-$(CONFIG_USER_ONLY))
|
||||
|
||||
ROMSUBDIR_RULES=$(patsubst %,romsubdir-%, $(ROMS))
|
||||
# Only keep -O and -g cflags
|
||||
|
@ -4,6 +4,8 @@ stub-obj-y = stubs/ crypto/
|
||||
util-obj-y = util/ qobject/ qapi/
|
||||
util-obj-y += qmp-introspect.o qapi-types.o qapi-visit.o qapi-event.o
|
||||
|
||||
chardev-obj-y = chardev/
|
||||
|
||||
#######################################################################
|
||||
# block-obj-y is code used by both qemu system emulation and qemu-img
|
||||
|
||||
@ -51,8 +53,7 @@ common-obj-$(CONFIG_POSIX) += os-posix.o
|
||||
common-obj-$(CONFIG_LINUX) += fsdev/
|
||||
|
||||
common-obj-y += migration/
|
||||
common-obj-y += qemu-char.o #aio.o
|
||||
common-obj-y += page_cache.o
|
||||
common-obj-y += page_cache.o #aio.o
|
||||
|
||||
common-obj-$(CONFIG_SPICE) += spice-qemu-char.o
|
||||
|
||||
|
@ -172,12 +172,14 @@ all-obj-y := $(obj-y)
|
||||
target-obj-y :=
|
||||
block-obj-y :=
|
||||
common-obj-y :=
|
||||
chardev-obj-y :=
|
||||
include $(SRC_PATH)/Makefile.objs
|
||||
dummy := $(call unnest-vars,,target-obj-y)
|
||||
target-obj-y-save := $(target-obj-y)
|
||||
dummy := $(call unnest-vars,.., \
|
||||
block-obj-y \
|
||||
block-obj-m \
|
||||
chardev-obj-y \
|
||||
crypto-obj-y \
|
||||
crypto-aes-obj-y \
|
||||
qom-obj-y \
|
||||
@ -188,7 +190,7 @@ target-obj-y := $(target-obj-y-save)
|
||||
all-obj-y += $(common-obj-y)
|
||||
all-obj-y += $(target-obj-y)
|
||||
all-obj-y += $(qom-obj-y)
|
||||
all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y)
|
||||
all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y) $(chardev-obj-y)
|
||||
all-obj-$(CONFIG_USER_ONLY) += $(crypto-aes-obj-y)
|
||||
all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y)
|
||||
all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y)
|
||||
|
@ -616,9 +616,9 @@ static void baum_chr_read(void *opaque)
|
||||
}
|
||||
}
|
||||
|
||||
static void baum_chr_free(Chardev *chr)
|
||||
static void char_braille_finalize(Object *obj)
|
||||
{
|
||||
BaumChardev *baum = BAUM_CHARDEV(chr);
|
||||
BaumChardev *baum = BAUM_CHARDEV(obj);
|
||||
|
||||
timer_free(baum->cellCount_timer);
|
||||
if (baum->brlapi) {
|
||||
@ -659,23 +659,18 @@ static void char_braille_class_init(ObjectClass *oc, void *data)
|
||||
cc->open = baum_chr_open;
|
||||
cc->chr_write = baum_chr_write;
|
||||
cc->chr_accept_input = baum_chr_accept_input;
|
||||
cc->chr_free = baum_chr_free;
|
||||
}
|
||||
|
||||
static const TypeInfo char_braille_type_info = {
|
||||
.name = TYPE_CHARDEV_BRAILLE,
|
||||
.parent = TYPE_CHARDEV,
|
||||
.instance_size = sizeof(BaumChardev),
|
||||
.instance_finalize = char_braille_finalize,
|
||||
.class_init = char_braille_class_init,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
static const CharDriver driver = {
|
||||
.kind = CHARDEV_BACKEND_KIND_BRAILLE,
|
||||
};
|
||||
|
||||
register_char_driver(&driver);
|
||||
type_register_static(&char_braille_type_info);
|
||||
}
|
||||
|
||||
|
@ -139,9 +139,9 @@ static int msmouse_chr_write(struct Chardev *s, const uint8_t *buf, int len)
|
||||
return len;
|
||||
}
|
||||
|
||||
static void msmouse_chr_free(struct Chardev *chr)
|
||||
static void char_msmouse_finalize(Object *obj)
|
||||
{
|
||||
MouseChardev *mouse = MOUSE_CHARDEV(chr);
|
||||
MouseChardev *mouse = MOUSE_CHARDEV(obj);
|
||||
|
||||
qemu_input_handler_unregister(mouse->hs);
|
||||
}
|
||||
@ -172,23 +172,18 @@ static void char_msmouse_class_init(ObjectClass *oc, void *data)
|
||||
cc->open = msmouse_chr_open;
|
||||
cc->chr_write = msmouse_chr_write;
|
||||
cc->chr_accept_input = msmouse_chr_accept_input;
|
||||
cc->chr_free = msmouse_chr_free;
|
||||
}
|
||||
|
||||
static const TypeInfo char_msmouse_type_info = {
|
||||
.name = TYPE_CHARDEV_MSMOUSE,
|
||||
.parent = TYPE_CHARDEV,
|
||||
.instance_size = sizeof(MouseChardev),
|
||||
.instance_finalize = char_msmouse_finalize,
|
||||
.class_init = char_msmouse_class_init,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
static const CharDriver driver = {
|
||||
.kind = CHARDEV_BACKEND_KIND_MSMOUSE,
|
||||
};
|
||||
|
||||
register_char_driver(&driver);
|
||||
type_register_static(&char_msmouse_type_info);
|
||||
}
|
||||
|
||||
|
@ -123,11 +123,6 @@ static const TypeInfo char_testdev_type_info = {
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
static const CharDriver driver = {
|
||||
.kind = CHARDEV_BACKEND_KIND_TESTDEV,
|
||||
};
|
||||
|
||||
register_char_driver(&driver);
|
||||
type_register_static(&char_testdev_type_info);
|
||||
}
|
||||
|
||||
|
17
chardev/Makefile.objs
Normal file
17
chardev/Makefile.objs
Normal file
@ -0,0 +1,17 @@
|
||||
chardev-obj-y += char.o
|
||||
chardev-obj-$(CONFIG_WIN32) += char-console.o
|
||||
chardev-obj-$(CONFIG_POSIX) += char-fd.o
|
||||
chardev-obj-y += char-file.o
|
||||
chardev-obj-y += char-io.o
|
||||
chardev-obj-y += char-mux.o
|
||||
chardev-obj-y += char-null.o
|
||||
chardev-obj-$(CONFIG_POSIX) += char-parallel.o
|
||||
chardev-obj-y += char-pipe.o
|
||||
chardev-obj-$(CONFIG_POSIX) += char-pty.o
|
||||
chardev-obj-y += char-ringbuf.o
|
||||
chardev-obj-y += char-serial.o
|
||||
chardev-obj-y += char-socket.o
|
||||
chardev-obj-y += char-stdio.o
|
||||
chardev-obj-y += char-udp.o
|
||||
chardev-obj-$(CONFIG_WIN32) += char-win.o
|
||||
chardev-obj-$(CONFIG_WIN32) += char-win-stdio.o
|
53
chardev/char-console.c
Normal file
53
chardev/char-console.c
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "char-win.h"
|
||||
|
||||
static void qemu_chr_open_win_con(Chardev *chr,
|
||||
ChardevBackend *backend,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
qemu_chr_open_win_file(chr, GetStdHandle(STD_OUTPUT_HANDLE));
|
||||
}
|
||||
|
||||
static void char_console_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->open = qemu_chr_open_win_con;
|
||||
}
|
||||
|
||||
static const TypeInfo char_console_type_info = {
|
||||
.name = TYPE_CHARDEV_CONSOLE,
|
||||
.parent = TYPE_CHARDEV_WIN,
|
||||
.class_init = char_console_class_init,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&char_console_type_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
170
chardev/char-fd.c
Normal file
170
chardev/char-fd.c
Normal file
@ -0,0 +1,170 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu-common.h"
|
||||
#include "sysemu/char.h"
|
||||
#include "io/channel-file.h"
|
||||
|
||||
#include "char-fd.h"
|
||||
#include "char-io.h"
|
||||
|
||||
/* Called with chr_write_lock held. */
|
||||
static int fd_chr_write(Chardev *chr, const uint8_t *buf, int len)
|
||||
{
|
||||
FDChardev *s = FD_CHARDEV(chr);
|
||||
|
||||
return io_channel_send(s->ioc_out, buf, len);
|
||||
}
|
||||
|
||||
static gboolean fd_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
|
||||
{
|
||||
Chardev *chr = CHARDEV(opaque);
|
||||
FDChardev *s = FD_CHARDEV(opaque);
|
||||
int len;
|
||||
uint8_t buf[CHR_READ_BUF_LEN];
|
||||
ssize_t ret;
|
||||
|
||||
len = sizeof(buf);
|
||||
if (len > s->max_size) {
|
||||
len = s->max_size;
|
||||
}
|
||||
if (len == 0) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
ret = qio_channel_read(
|
||||
chan, (gchar *)buf, len, NULL);
|
||||
if (ret == 0) {
|
||||
remove_fd_in_watch(chr);
|
||||
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
||||
return FALSE;
|
||||
}
|
||||
if (ret > 0) {
|
||||
qemu_chr_be_write(chr, buf, ret);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int fd_chr_read_poll(void *opaque)
|
||||
{
|
||||
Chardev *chr = CHARDEV(opaque);
|
||||
FDChardev *s = FD_CHARDEV(opaque);
|
||||
|
||||
s->max_size = qemu_chr_be_can_write(chr);
|
||||
return s->max_size;
|
||||
}
|
||||
|
||||
static GSource *fd_chr_add_watch(Chardev *chr, GIOCondition cond)
|
||||
{
|
||||
FDChardev *s = FD_CHARDEV(chr);
|
||||
return qio_channel_create_watch(s->ioc_out, cond);
|
||||
}
|
||||
|
||||
static void fd_chr_update_read_handler(Chardev *chr,
|
||||
GMainContext *context)
|
||||
{
|
||||
FDChardev *s = FD_CHARDEV(chr);
|
||||
|
||||
remove_fd_in_watch(chr);
|
||||
if (s->ioc_in) {
|
||||
chr->fd_in_tag = io_add_watch_poll(chr, s->ioc_in,
|
||||
fd_chr_read_poll,
|
||||
fd_chr_read, chr,
|
||||
context);
|
||||
}
|
||||
}
|
||||
|
||||
static void char_fd_finalize(Object *obj)
|
||||
{
|
||||
Chardev *chr = CHARDEV(obj);
|
||||
FDChardev *s = FD_CHARDEV(obj);
|
||||
|
||||
remove_fd_in_watch(chr);
|
||||
if (s->ioc_in) {
|
||||
object_unref(OBJECT(s->ioc_in));
|
||||
}
|
||||
if (s->ioc_out) {
|
||||
object_unref(OBJECT(s->ioc_out));
|
||||
}
|
||||
|
||||
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
||||
}
|
||||
|
||||
int qmp_chardev_open_file_source(char *src, int flags, Error **errp)
|
||||
{
|
||||
int fd = -1;
|
||||
|
||||
TFR(fd = qemu_open(src, flags, 0666));
|
||||
if (fd == -1) {
|
||||
error_setg_file_open(errp, errno, src);
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
/* open a character device to a unix fd */
|
||||
void qemu_chr_open_fd(Chardev *chr,
|
||||
int fd_in, int fd_out)
|
||||
{
|
||||
FDChardev *s = FD_CHARDEV(chr);
|
||||
char *name;
|
||||
|
||||
s->ioc_in = QIO_CHANNEL(qio_channel_file_new_fd(fd_in));
|
||||
name = g_strdup_printf("chardev-file-in-%s", chr->label);
|
||||
qio_channel_set_name(QIO_CHANNEL(s->ioc_in), name);
|
||||
g_free(name);
|
||||
s->ioc_out = QIO_CHANNEL(qio_channel_file_new_fd(fd_out));
|
||||
name = g_strdup_printf("chardev-file-out-%s", chr->label);
|
||||
qio_channel_set_name(QIO_CHANNEL(s->ioc_out), name);
|
||||
g_free(name);
|
||||
qemu_set_nonblock(fd_out);
|
||||
s->chr = chr;
|
||||
}
|
||||
|
||||
static void char_fd_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->chr_add_watch = fd_chr_add_watch;
|
||||
cc->chr_write = fd_chr_write;
|
||||
cc->chr_update_read_handler = fd_chr_update_read_handler;
|
||||
}
|
||||
|
||||
static const TypeInfo char_fd_type_info = {
|
||||
.name = TYPE_CHARDEV_FD,
|
||||
.parent = TYPE_CHARDEV,
|
||||
.instance_size = sizeof(FDChardev),
|
||||
.instance_finalize = char_fd_finalize,
|
||||
.class_init = char_fd_class_init,
|
||||
.abstract = true,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&char_fd_type_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
44
chardev/char-fd.h
Normal file
44
chardev/char-fd.h
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef CHAR_FD_H
|
||||
#define CHAR_FD_H
|
||||
|
||||
#include "io/channel.h"
|
||||
#include "sysemu/char.h"
|
||||
|
||||
typedef struct FDChardev {
|
||||
Chardev parent;
|
||||
Chardev *chr;
|
||||
QIOChannel *ioc_in, *ioc_out;
|
||||
int max_size;
|
||||
} FDChardev;
|
||||
|
||||
#define TYPE_CHARDEV_FD "chardev-fd"
|
||||
|
||||
#define FD_CHARDEV(obj) OBJECT_CHECK(FDChardev, (obj), TYPE_CHARDEV_FD)
|
||||
|
||||
void qemu_chr_open_fd(Chardev *chr, int fd_in, int fd_out);
|
||||
int qmp_chardev_open_file_source(char *src, int flags, Error **errp);
|
||||
|
||||
#endif /* CHAR_FD_H */
|
139
chardev/char-file.c
Normal file
139
chardev/char-file.c
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu-common.h"
|
||||
#include "sysemu/char.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "char-win.h"
|
||||
#else
|
||||
#include "char-fd.h"
|
||||
#endif
|
||||
|
||||
static void qmp_chardev_open_file(Chardev *chr,
|
||||
ChardevBackend *backend,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
ChardevFile *file = backend->u.file.data;
|
||||
#ifdef _WIN32
|
||||
HANDLE out;
|
||||
DWORD accessmode;
|
||||
DWORD flags;
|
||||
|
||||
if (file->has_in) {
|
||||
error_setg(errp, "input file not supported");
|
||||
return;
|
||||
}
|
||||
|
||||
if (file->has_append && file->append) {
|
||||
/* Append to file if it already exists. */
|
||||
accessmode = FILE_GENERIC_WRITE & ~FILE_WRITE_DATA;
|
||||
flags = OPEN_ALWAYS;
|
||||
} else {
|
||||
/* Truncate file if it already exists. */
|
||||
accessmode = GENERIC_WRITE;
|
||||
flags = CREATE_ALWAYS;
|
||||
}
|
||||
|
||||
out = CreateFile(file->out, accessmode, FILE_SHARE_READ, NULL, flags,
|
||||
FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (out == INVALID_HANDLE_VALUE) {
|
||||
error_setg(errp, "open %s failed", file->out);
|
||||
return;
|
||||
}
|
||||
|
||||
qemu_chr_open_win_file(chr, out);
|
||||
#else
|
||||
int flags, in = -1, out;
|
||||
|
||||
flags = O_WRONLY | O_CREAT | O_BINARY;
|
||||
if (file->has_append && file->append) {
|
||||
flags |= O_APPEND;
|
||||
} else {
|
||||
flags |= O_TRUNC;
|
||||
}
|
||||
|
||||
out = qmp_chardev_open_file_source(file->out, flags, errp);
|
||||
if (out < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (file->has_in) {
|
||||
flags = O_RDONLY;
|
||||
in = qmp_chardev_open_file_source(file->in, flags, errp);
|
||||
if (in < 0) {
|
||||
qemu_close(out);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
qemu_chr_open_fd(chr, in, out);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void qemu_chr_parse_file_out(QemuOpts *opts, ChardevBackend *backend,
|
||||
Error **errp)
|
||||
{
|
||||
const char *path = qemu_opt_get(opts, "path");
|
||||
ChardevFile *file;
|
||||
|
||||
backend->type = CHARDEV_BACKEND_KIND_FILE;
|
||||
if (path == NULL) {
|
||||
error_setg(errp, "chardev: file: no filename given");
|
||||
return;
|
||||
}
|
||||
file = backend->u.file.data = g_new0(ChardevFile, 1);
|
||||
qemu_chr_parse_common(opts, qapi_ChardevFile_base(file));
|
||||
file->out = g_strdup(path);
|
||||
|
||||
file->has_append = true;
|
||||
file->append = qemu_opt_get_bool(opts, "append", false);
|
||||
}
|
||||
|
||||
static void char_file_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->parse = qemu_chr_parse_file_out;
|
||||
cc->open = qmp_chardev_open_file;
|
||||
}
|
||||
|
||||
static const TypeInfo char_file_type_info = {
|
||||
.name = TYPE_CHARDEV_FILE,
|
||||
#ifdef _WIN32
|
||||
.parent = TYPE_CHARDEV_WIN,
|
||||
#else
|
||||
.parent = TYPE_CHARDEV_FD,
|
||||
#endif
|
||||
.class_init = char_file_class_init,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&char_file_type_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
192
chardev/char-io.c
Normal file
192
chardev/char-io.c
Normal file
@ -0,0 +1,192 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "char-io.h"
|
||||
|
||||
typedef struct IOWatchPoll {
|
||||
GSource parent;
|
||||
|
||||
QIOChannel *ioc;
|
||||
GSource *src;
|
||||
|
||||
IOCanReadHandler *fd_can_read;
|
||||
GSourceFunc fd_read;
|
||||
void *opaque;
|
||||
GMainContext *context;
|
||||
} IOWatchPoll;
|
||||
|
||||
static IOWatchPoll *io_watch_poll_from_source(GSource *source)
|
||||
{
|
||||
return container_of(source, IOWatchPoll, parent);
|
||||
}
|
||||
|
||||
static gboolean io_watch_poll_prepare(GSource *source,
|
||||
gint *timeout)
|
||||
{
|
||||
IOWatchPoll *iwp = io_watch_poll_from_source(source);
|
||||
bool now_active = iwp->fd_can_read(iwp->opaque) > 0;
|
||||
bool was_active = iwp->src != NULL;
|
||||
if (was_active == now_active) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (now_active) {
|
||||
iwp->src = qio_channel_create_watch(
|
||||
iwp->ioc, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL);
|
||||
g_source_set_callback(iwp->src, iwp->fd_read, iwp->opaque, NULL);
|
||||
g_source_attach(iwp->src, iwp->context);
|
||||
} else {
|
||||
g_source_destroy(iwp->src);
|
||||
g_source_unref(iwp->src);
|
||||
iwp->src = NULL;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean io_watch_poll_check(GSource *source)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean io_watch_poll_dispatch(GSource *source, GSourceFunc callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
static void io_watch_poll_finalize(GSource *source)
|
||||
{
|
||||
/* Due to a glib bug, removing the last reference to a source
|
||||
* inside a finalize callback causes recursive locking (and a
|
||||
* deadlock). This is not a problem inside other callbacks,
|
||||
* including dispatch callbacks, so we call io_remove_watch_poll
|
||||
* to remove this source. At this point, iwp->src must
|
||||
* be NULL, or we would leak it.
|
||||
*
|
||||
* This would be solved much more elegantly by child sources,
|
||||
* but we support older glib versions that do not have them.
|
||||
*/
|
||||
IOWatchPoll *iwp = io_watch_poll_from_source(source);
|
||||
assert(iwp->src == NULL);
|
||||
}
|
||||
|
||||
static GSourceFuncs io_watch_poll_funcs = {
|
||||
.prepare = io_watch_poll_prepare,
|
||||
.check = io_watch_poll_check,
|
||||
.dispatch = io_watch_poll_dispatch,
|
||||
.finalize = io_watch_poll_finalize,
|
||||
};
|
||||
|
||||
guint io_add_watch_poll(Chardev *chr,
|
||||
QIOChannel *ioc,
|
||||
IOCanReadHandler *fd_can_read,
|
||||
QIOChannelFunc fd_read,
|
||||
gpointer user_data,
|
||||
GMainContext *context)
|
||||
{
|
||||
IOWatchPoll *iwp;
|
||||
int tag;
|
||||
char *name;
|
||||
|
||||
iwp = (IOWatchPoll *) g_source_new(&io_watch_poll_funcs,
|
||||
sizeof(IOWatchPoll));
|
||||
iwp->fd_can_read = fd_can_read;
|
||||
iwp->opaque = user_data;
|
||||
iwp->ioc = ioc;
|
||||
iwp->fd_read = (GSourceFunc) fd_read;
|
||||
iwp->src = NULL;
|
||||
iwp->context = context;
|
||||
|
||||
name = g_strdup_printf("chardev-iowatch-%s", chr->label);
|
||||
g_source_set_name((GSource *)iwp, name);
|
||||
g_free(name);
|
||||
|
||||
tag = g_source_attach(&iwp->parent, context);
|
||||
g_source_unref(&iwp->parent);
|
||||
return tag;
|
||||
}
|
||||
|
||||
static void io_remove_watch_poll(guint tag)
|
||||
{
|
||||
GSource *source;
|
||||
IOWatchPoll *iwp;
|
||||
|
||||
g_return_if_fail(tag > 0);
|
||||
|
||||
source = g_main_context_find_source_by_id(NULL, tag);
|
||||
g_return_if_fail(source != NULL);
|
||||
|
||||
iwp = io_watch_poll_from_source(source);
|
||||
if (iwp->src) {
|
||||
g_source_destroy(iwp->src);
|
||||
g_source_unref(iwp->src);
|
||||
iwp->src = NULL;
|
||||
}
|
||||
g_source_destroy(&iwp->parent);
|
||||
}
|
||||
|
||||
void remove_fd_in_watch(Chardev *chr)
|
||||
{
|
||||
if (chr->fd_in_tag) {
|
||||
io_remove_watch_poll(chr->fd_in_tag);
|
||||
chr->fd_in_tag = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int io_channel_send_full(QIOChannel *ioc,
|
||||
const void *buf, size_t len,
|
||||
int *fds, size_t nfds)
|
||||
{
|
||||
size_t offset = 0;
|
||||
|
||||
while (offset < len) {
|
||||
ssize_t ret = 0;
|
||||
struct iovec iov = { .iov_base = (char *)buf + offset,
|
||||
.iov_len = len - offset };
|
||||
|
||||
ret = qio_channel_writev_full(
|
||||
ioc, &iov, 1,
|
||||
fds, nfds, NULL);
|
||||
if (ret == QIO_CHANNEL_ERR_BLOCK) {
|
||||
if (offset) {
|
||||
return offset;
|
||||
}
|
||||
|
||||
errno = EAGAIN;
|
||||
return -1;
|
||||
} else if (ret < 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
offset += ret;
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
int io_channel_send(QIOChannel *ioc, const void *buf, size_t len)
|
||||
{
|
||||
return io_channel_send_full(ioc, buf, len, NULL, 0);
|
||||
}
|
46
chardev/char-io.h
Normal file
46
chardev/char-io.h
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef CHAR_IO_H
|
||||
#define CHAR_IO_H
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "io/channel.h"
|
||||
#include "sysemu/char.h"
|
||||
|
||||
/* Can only be used for read */
|
||||
guint io_add_watch_poll(Chardev *chr,
|
||||
QIOChannel *ioc,
|
||||
IOCanReadHandler *fd_can_read,
|
||||
QIOChannelFunc fd_read,
|
||||
gpointer user_data,
|
||||
GMainContext *context);
|
||||
|
||||
void remove_fd_in_watch(Chardev *chr);
|
||||
|
||||
int io_channel_send(QIOChannel *ioc, const void *buf, size_t len);
|
||||
|
||||
int io_channel_send_full(QIOChannel *ioc, const void *buf, size_t len,
|
||||
int *fds, size_t nfds);
|
||||
|
||||
#endif /* CHAR_IO_H */
|
358
chardev/char-mux.c
Normal file
358
chardev/char-mux.c
Normal file
@ -0,0 +1,358 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu-common.h"
|
||||
#include "sysemu/char.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "char-mux.h"
|
||||
|
||||
/* MUX driver for serial I/O splitting */
|
||||
|
||||
/* Called with chr_write_lock held. */
|
||||
static int mux_chr_write(Chardev *chr, const uint8_t *buf, int len)
|
||||
{
|
||||
MuxChardev *d = MUX_CHARDEV(chr);
|
||||
int ret;
|
||||
if (!d->timestamps) {
|
||||
ret = qemu_chr_fe_write(&d->chr, buf, len);
|
||||
} else {
|
||||
int i;
|
||||
|
||||
ret = 0;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (d->linestart) {
|
||||
char buf1[64];
|
||||
int64_t ti;
|
||||
int secs;
|
||||
|
||||
ti = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
|
||||
if (d->timestamps_start == -1) {
|
||||
d->timestamps_start = ti;
|
||||
}
|
||||
ti -= d->timestamps_start;
|
||||
secs = ti / 1000;
|
||||
snprintf(buf1, sizeof(buf1),
|
||||
"[%02d:%02d:%02d.%03d] ",
|
||||
secs / 3600,
|
||||
(secs / 60) % 60,
|
||||
secs % 60,
|
||||
(int)(ti % 1000));
|
||||
/* XXX this blocks entire thread. Rewrite to use
|
||||
* qemu_chr_fe_write and background I/O callbacks */
|
||||
qemu_chr_fe_write_all(&d->chr,
|
||||
(uint8_t *)buf1, strlen(buf1));
|
||||
d->linestart = 0;
|
||||
}
|
||||
ret += qemu_chr_fe_write(&d->chr, buf + i, 1);
|
||||
if (buf[i] == '\n') {
|
||||
d->linestart = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char * const mux_help[] = {
|
||||
"% h print this help\n\r",
|
||||
"% x exit emulator\n\r",
|
||||
"% s save disk data back to file (if -snapshot)\n\r",
|
||||
"% t toggle console timestamps\n\r",
|
||||
"% b send break (magic sysrq)\n\r",
|
||||
"% c switch between console and monitor\n\r",
|
||||
"% % sends %\n\r",
|
||||
NULL
|
||||
};
|
||||
|
||||
int term_escape_char = 0x01; /* ctrl-a is used for escape */
|
||||
static void mux_print_help(Chardev *chr)
|
||||
{
|
||||
int i, j;
|
||||
char ebuf[15] = "Escape-Char";
|
||||
char cbuf[50] = "\n\r";
|
||||
|
||||
if (term_escape_char > 0 && term_escape_char < 26) {
|
||||
snprintf(cbuf, sizeof(cbuf), "\n\r");
|
||||
snprintf(ebuf, sizeof(ebuf), "C-%c", term_escape_char - 1 + 'a');
|
||||
} else {
|
||||
snprintf(cbuf, sizeof(cbuf),
|
||||
"\n\rEscape-Char set to Ascii: 0x%02x\n\r\n\r",
|
||||
term_escape_char);
|
||||
}
|
||||
/* XXX this blocks entire thread. Rewrite to use
|
||||
* qemu_chr_fe_write and background I/O callbacks */
|
||||
qemu_chr_write_all(chr, (uint8_t *)cbuf, strlen(cbuf));
|
||||
for (i = 0; mux_help[i] != NULL; i++) {
|
||||
for (j = 0; mux_help[i][j] != '\0'; j++) {
|
||||
if (mux_help[i][j] == '%') {
|
||||
qemu_chr_write_all(chr, (uint8_t *)ebuf, strlen(ebuf));
|
||||
} else {
|
||||
qemu_chr_write_all(chr, (uint8_t *)&mux_help[i][j], 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mux_chr_send_event(MuxChardev *d, int mux_nr, int event)
|
||||
{
|
||||
CharBackend *be = d->backends[mux_nr];
|
||||
|
||||
if (be && be->chr_event) {
|
||||
be->chr_event(be->opaque, event);
|
||||
}
|
||||
}
|
||||
|
||||
static int mux_proc_byte(Chardev *chr, MuxChardev *d, int ch)
|
||||
{
|
||||
if (d->term_got_escape) {
|
||||
d->term_got_escape = 0;
|
||||
if (ch == term_escape_char) {
|
||||
goto send_char;
|
||||
}
|
||||
switch (ch) {
|
||||
case '?':
|
||||
case 'h':
|
||||
mux_print_help(chr);
|
||||
break;
|
||||
case 'x':
|
||||
{
|
||||
const char *term = "QEMU: Terminated\n\r";
|
||||
qemu_chr_write_all(chr, (uint8_t *)term, strlen(term));
|
||||
exit(0);
|
||||
break;
|
||||
}
|
||||
case 's':
|
||||
blk_commit_all();
|
||||
break;
|
||||
case 'b':
|
||||
qemu_chr_be_event(chr, CHR_EVENT_BREAK);
|
||||
break;
|
||||
case 'c':
|
||||
assert(d->mux_cnt > 0); /* handler registered with first fe */
|
||||
/* Switch to the next registered device */
|
||||
mux_set_focus(chr, (d->focus + 1) % d->mux_cnt);
|
||||
break;
|
||||
case 't':
|
||||
d->timestamps = !d->timestamps;
|
||||
d->timestamps_start = -1;
|
||||
d->linestart = 0;
|
||||
break;
|
||||
}
|
||||
} else if (ch == term_escape_char) {
|
||||
d->term_got_escape = 1;
|
||||
} else {
|
||||
send_char:
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mux_chr_accept_input(Chardev *chr)
|
||||
{
|
||||
MuxChardev *d = MUX_CHARDEV(chr);
|
||||
int m = d->focus;
|
||||
CharBackend *be = d->backends[m];
|
||||
|
||||
while (be && d->prod[m] != d->cons[m] &&
|
||||
be->chr_can_read && be->chr_can_read(be->opaque)) {
|
||||
be->chr_read(be->opaque,
|
||||
&d->buffer[m][d->cons[m]++ & MUX_BUFFER_MASK], 1);
|
||||
}
|
||||
}
|
||||
|
||||
static int mux_chr_can_read(void *opaque)
|
||||
{
|
||||
MuxChardev *d = MUX_CHARDEV(opaque);
|
||||
int m = d->focus;
|
||||
CharBackend *be = d->backends[m];
|
||||
|
||||
if ((d->prod[m] - d->cons[m]) < MUX_BUFFER_SIZE) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (be && be->chr_can_read) {
|
||||
return be->chr_can_read(be->opaque);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mux_chr_read(void *opaque, const uint8_t *buf, int size)
|
||||
{
|
||||
Chardev *chr = CHARDEV(opaque);
|
||||
MuxChardev *d = MUX_CHARDEV(opaque);
|
||||
int m = d->focus;
|
||||
CharBackend *be = d->backends[m];
|
||||
int i;
|
||||
|
||||
mux_chr_accept_input(opaque);
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
if (mux_proc_byte(chr, d, buf[i])) {
|
||||
if (d->prod[m] == d->cons[m] &&
|
||||
be && be->chr_can_read &&
|
||||
be->chr_can_read(be->opaque)) {
|
||||
be->chr_read(be->opaque, &buf[i], 1);
|
||||
} else {
|
||||
d->buffer[m][d->prod[m]++ & MUX_BUFFER_MASK] = buf[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool muxes_realized;
|
||||
|
||||
static void mux_chr_event(void *opaque, int event)
|
||||
{
|
||||
MuxChardev *d = MUX_CHARDEV(opaque);
|
||||
int i;
|
||||
|
||||
if (!muxes_realized) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Send the event to all registered listeners */
|
||||
for (i = 0; i < d->mux_cnt; i++) {
|
||||
mux_chr_send_event(d, i, event);
|
||||
}
|
||||
}
|
||||
|
||||
static GSource *mux_chr_add_watch(Chardev *s, GIOCondition cond)
|
||||
{
|
||||
MuxChardev *d = MUX_CHARDEV(s);
|
||||
Chardev *chr = qemu_chr_fe_get_driver(&d->chr);
|
||||
ChardevClass *cc = CHARDEV_GET_CLASS(chr);
|
||||
|
||||
if (!cc->chr_add_watch) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return cc->chr_add_watch(chr, cond);
|
||||
}
|
||||
|
||||
static void char_mux_finalize(Object *obj)
|
||||
{
|
||||
MuxChardev *d = MUX_CHARDEV(obj);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < d->mux_cnt; i++) {
|
||||
CharBackend *be = d->backends[i];
|
||||
if (be) {
|
||||
be->chr = NULL;
|
||||
}
|
||||
}
|
||||
qemu_chr_fe_deinit(&d->chr);
|
||||
}
|
||||
|
||||
void mux_chr_set_handlers(Chardev *chr, GMainContext *context)
|
||||
{
|
||||
MuxChardev *d = MUX_CHARDEV(chr);
|
||||
|
||||
/* Fix up the real driver with mux routines */
|
||||
qemu_chr_fe_set_handlers(&d->chr,
|
||||
mux_chr_can_read,
|
||||
mux_chr_read,
|
||||
mux_chr_event,
|
||||
chr,
|
||||
context, true);
|
||||
}
|
||||
|
||||
void mux_set_focus(Chardev *chr, int focus)
|
||||
{
|
||||
MuxChardev *d = MUX_CHARDEV(chr);
|
||||
|
||||
assert(focus >= 0);
|
||||
assert(focus < d->mux_cnt);
|
||||
|
||||
if (d->focus != -1) {
|
||||
mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT);
|
||||
}
|
||||
|
||||
d->focus = focus;
|
||||
mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN);
|
||||
}
|
||||
|
||||
static void qemu_chr_open_mux(Chardev *chr,
|
||||
ChardevBackend *backend,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
ChardevMux *mux = backend->u.mux.data;
|
||||
Chardev *drv;
|
||||
MuxChardev *d = MUX_CHARDEV(chr);
|
||||
|
||||
drv = qemu_chr_find(mux->chardev);
|
||||
if (drv == NULL) {
|
||||
error_setg(errp, "mux: base chardev %s not found", mux->chardev);
|
||||
return;
|
||||
}
|
||||
|
||||
d->focus = -1;
|
||||
/* only default to opened state if we've realized the initial
|
||||
* set of muxes
|
||||
*/
|
||||
*be_opened = muxes_realized;
|
||||
qemu_chr_fe_init(&d->chr, drv, errp);
|
||||
}
|
||||
|
||||
static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend,
|
||||
Error **errp)
|
||||
{
|
||||
const char *chardev = qemu_opt_get(opts, "chardev");
|
||||
ChardevMux *mux;
|
||||
|
||||
if (chardev == NULL) {
|
||||
error_setg(errp, "chardev: mux: no chardev given");
|
||||
return;
|
||||
}
|
||||
backend->type = CHARDEV_BACKEND_KIND_MUX;
|
||||
mux = backend->u.mux.data = g_new0(ChardevMux, 1);
|
||||
qemu_chr_parse_common(opts, qapi_ChardevMux_base(mux));
|
||||
mux->chardev = g_strdup(chardev);
|
||||
}
|
||||
|
||||
static void char_mux_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->parse = qemu_chr_parse_mux;
|
||||
cc->open = qemu_chr_open_mux;
|
||||
cc->chr_write = mux_chr_write;
|
||||
cc->chr_accept_input = mux_chr_accept_input;
|
||||
cc->chr_add_watch = mux_chr_add_watch;
|
||||
}
|
||||
|
||||
static const TypeInfo char_mux_type_info = {
|
||||
.name = TYPE_CHARDEV_MUX,
|
||||
.parent = TYPE_CHARDEV,
|
||||
.class_init = char_mux_class_init,
|
||||
.instance_size = sizeof(MuxChardev),
|
||||
.instance_finalize = char_mux_finalize,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&char_mux_type_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
63
chardev/char-mux.h
Normal file
63
chardev/char-mux.h
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef CHAR_MUX_H
|
||||
#define CHAR_MUX_H
|
||||
|
||||
#include "sysemu/char.h"
|
||||
|
||||
extern bool muxes_realized;
|
||||
|
||||
#define MAX_MUX 4
|
||||
#define MUX_BUFFER_SIZE 32 /* Must be a power of 2. */
|
||||
#define MUX_BUFFER_MASK (MUX_BUFFER_SIZE - 1)
|
||||
typedef struct MuxChardev {
|
||||
Chardev parent;
|
||||
CharBackend *backends[MAX_MUX];
|
||||
CharBackend chr;
|
||||
int focus;
|
||||
int mux_cnt;
|
||||
int term_got_escape;
|
||||
int max_size;
|
||||
/* Intermediate input buffer catches escape sequences even if the
|
||||
currently active device is not accepting any input - but only until it
|
||||
is full as well. */
|
||||
unsigned char buffer[MAX_MUX][MUX_BUFFER_SIZE];
|
||||
int prod[MAX_MUX];
|
||||
int cons[MAX_MUX];
|
||||
int timestamps;
|
||||
|
||||
/* Protected by the Chardev chr_write_lock. */
|
||||
int linestart;
|
||||
int64_t timestamps_start;
|
||||
} MuxChardev;
|
||||
|
||||
#define MUX_CHARDEV(obj) OBJECT_CHECK(MuxChardev, (obj), TYPE_CHARDEV_MUX)
|
||||
#define CHARDEV_IS_MUX(chr) \
|
||||
object_dynamic_cast(OBJECT(chr), TYPE_CHARDEV_MUX)
|
||||
|
||||
void mux_chr_set_handlers(Chardev *chr, GMainContext *context);
|
||||
void mux_set_focus(Chardev *chr, int focus);
|
||||
void mux_chr_send_event(MuxChardev *d, int mux_nr, int event);
|
||||
|
||||
#endif /* CHAR_MUX_H */
|
54
chardev/char-null.c
Normal file
54
chardev/char-null.c
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/char.h"
|
||||
|
||||
static void null_chr_open(Chardev *chr,
|
||||
ChardevBackend *backend,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
*be_opened = false;
|
||||
}
|
||||
|
||||
static void char_null_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->open = null_chr_open;
|
||||
}
|
||||
|
||||
static const TypeInfo char_null_type_info = {
|
||||
.name = TYPE_CHARDEV_NULL,
|
||||
.parent = TYPE_CHARDEV,
|
||||
.instance_size = sizeof(Chardev),
|
||||
.class_init = char_null_class_init,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&char_null_type_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
316
chardev/char-parallel.c
Normal file
316
chardev/char-parallel.c
Normal file
@ -0,0 +1,316 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/char.h"
|
||||
#include "qapi/error.h"
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#ifdef CONFIG_BSD
|
||||
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
||||
#include <dev/ppbus/ppi.h>
|
||||
#include <dev/ppbus/ppbconf.h>
|
||||
#elif defined(__DragonFly__)
|
||||
#include <dev/misc/ppi/ppi.h>
|
||||
#include <bus/ppbus/ppbconf.h>
|
||||
#endif
|
||||
#else
|
||||
#ifdef __linux__
|
||||
#include <linux/ppdev.h>
|
||||
#include <linux/parport.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "char-fd.h"
|
||||
#include "char-parallel.h"
|
||||
|
||||
#if defined(__linux__)
|
||||
|
||||
typedef struct {
|
||||
Chardev parent;
|
||||
int fd;
|
||||
int mode;
|
||||
} ParallelChardev;
|
||||
|
||||
#define PARALLEL_CHARDEV(obj) \
|
||||
OBJECT_CHECK(ParallelChardev, (obj), TYPE_CHARDEV_PARALLEL)
|
||||
|
||||
static int pp_hw_mode(ParallelChardev *s, uint16_t mode)
|
||||
{
|
||||
if (s->mode != mode) {
|
||||
int m = mode;
|
||||
if (ioctl(s->fd, PPSETMODE, &m) < 0) {
|
||||
return 0;
|
||||
}
|
||||
s->mode = mode;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int pp_ioctl(Chardev *chr, int cmd, void *arg)
|
||||
{
|
||||
ParallelChardev *drv = PARALLEL_CHARDEV(chr);
|
||||
int fd = drv->fd;
|
||||
uint8_t b;
|
||||
|
||||
switch (cmd) {
|
||||
case CHR_IOCTL_PP_READ_DATA:
|
||||
if (ioctl(fd, PPRDATA, &b) < 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
*(uint8_t *)arg = b;
|
||||
break;
|
||||
case CHR_IOCTL_PP_WRITE_DATA:
|
||||
b = *(uint8_t *)arg;
|
||||
if (ioctl(fd, PPWDATA, &b) < 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
break;
|
||||
case CHR_IOCTL_PP_READ_CONTROL:
|
||||
if (ioctl(fd, PPRCONTROL, &b) < 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
/* Linux gives only the lowest bits, and no way to know data
|
||||
direction! For better compatibility set the fixed upper
|
||||
bits. */
|
||||
*(uint8_t *)arg = b | 0xc0;
|
||||
break;
|
||||
case CHR_IOCTL_PP_WRITE_CONTROL:
|
||||
b = *(uint8_t *)arg;
|
||||
if (ioctl(fd, PPWCONTROL, &b) < 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
break;
|
||||
case CHR_IOCTL_PP_READ_STATUS:
|
||||
if (ioctl(fd, PPRSTATUS, &b) < 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
*(uint8_t *)arg = b;
|
||||
break;
|
||||
case CHR_IOCTL_PP_DATA_DIR:
|
||||
if (ioctl(fd, PPDATADIR, (int *)arg) < 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
break;
|
||||
case CHR_IOCTL_PP_EPP_READ_ADDR:
|
||||
if (pp_hw_mode(drv, IEEE1284_MODE_EPP | IEEE1284_ADDR)) {
|
||||
struct ParallelIOArg *parg = arg;
|
||||
int n = read(fd, parg->buffer, parg->count);
|
||||
if (n != parg->count) {
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CHR_IOCTL_PP_EPP_READ:
|
||||
if (pp_hw_mode(drv, IEEE1284_MODE_EPP)) {
|
||||
struct ParallelIOArg *parg = arg;
|
||||
int n = read(fd, parg->buffer, parg->count);
|
||||
if (n != parg->count) {
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CHR_IOCTL_PP_EPP_WRITE_ADDR:
|
||||
if (pp_hw_mode(drv, IEEE1284_MODE_EPP | IEEE1284_ADDR)) {
|
||||
struct ParallelIOArg *parg = arg;
|
||||
int n = write(fd, parg->buffer, parg->count);
|
||||
if (n != parg->count) {
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CHR_IOCTL_PP_EPP_WRITE:
|
||||
if (pp_hw_mode(drv, IEEE1284_MODE_EPP)) {
|
||||
struct ParallelIOArg *parg = arg;
|
||||
int n = write(fd, parg->buffer, parg->count);
|
||||
if (n != parg->count) {
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qemu_chr_open_pp_fd(Chardev *chr,
|
||||
int fd,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
ParallelChardev *drv = PARALLEL_CHARDEV(chr);
|
||||
|
||||
if (ioctl(fd, PPCLAIM) < 0) {
|
||||
error_setg_errno(errp, errno, "not a parallel port");
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
drv->fd = fd;
|
||||
drv->mode = IEEE1284_MODE_COMPAT;
|
||||
}
|
||||
#endif /* __linux__ */
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
|
||||
|
||||
typedef struct {
|
||||
Chardev parent;
|
||||
int fd;
|
||||
} ParallelChardev;
|
||||
|
||||
#define PARALLEL_CHARDEV(obj) \
|
||||
OBJECT_CHECK(ParallelChardev, (obj), TYPE_CHARDEV_PARALLEL)
|
||||
|
||||
static int pp_ioctl(Chardev *chr, int cmd, void *arg)
|
||||
{
|
||||
ParallelChardev *drv = PARALLEL_CHARDEV(chr);
|
||||
uint8_t b;
|
||||
|
||||
switch (cmd) {
|
||||
case CHR_IOCTL_PP_READ_DATA:
|
||||
if (ioctl(drv->fd, PPIGDATA, &b) < 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
*(uint8_t *)arg = b;
|
||||
break;
|
||||
case CHR_IOCTL_PP_WRITE_DATA:
|
||||
b = *(uint8_t *)arg;
|
||||
if (ioctl(drv->fd, PPISDATA, &b) < 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
break;
|
||||
case CHR_IOCTL_PP_READ_CONTROL:
|
||||
if (ioctl(drv->fd, PPIGCTRL, &b) < 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
*(uint8_t *)arg = b;
|
||||
break;
|
||||
case CHR_IOCTL_PP_WRITE_CONTROL:
|
||||
b = *(uint8_t *)arg;
|
||||
if (ioctl(drv->fd, PPISCTRL, &b) < 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
break;
|
||||
case CHR_IOCTL_PP_READ_STATUS:
|
||||
if (ioctl(drv->fd, PPIGSTATUS, &b) < 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
*(uint8_t *)arg = b;
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qemu_chr_open_pp_fd(Chardev *chr,
|
||||
int fd,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
ParallelChardev *drv = PARALLEL_CHARDEV(chr);
|
||||
drv->fd = fd;
|
||||
*be_opened = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CHARDEV_PARPORT
|
||||
static void qmp_chardev_open_parallel(Chardev *chr,
|
||||
ChardevBackend *backend,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
ChardevHostdev *parallel = backend->u.parallel.data;
|
||||
int fd;
|
||||
|
||||
fd = qmp_chardev_open_file_source(parallel->device, O_RDWR, errp);
|
||||
if (fd < 0) {
|
||||
return;
|
||||
}
|
||||
qemu_chr_open_pp_fd(chr, fd, be_opened, errp);
|
||||
}
|
||||
|
||||
static void qemu_chr_parse_parallel(QemuOpts *opts, ChardevBackend *backend,
|
||||
Error **errp)
|
||||
{
|
||||
const char *device = qemu_opt_get(opts, "path");
|
||||
ChardevHostdev *parallel;
|
||||
|
||||
if (device == NULL) {
|
||||
error_setg(errp, "chardev: parallel: no device path given");
|
||||
return;
|
||||
}
|
||||
backend->type = CHARDEV_BACKEND_KIND_PARALLEL;
|
||||
parallel = backend->u.parallel.data = g_new0(ChardevHostdev, 1);
|
||||
qemu_chr_parse_common(opts, qapi_ChardevHostdev_base(parallel));
|
||||
parallel->device = g_strdup(device);
|
||||
}
|
||||
|
||||
static void char_parallel_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->parse = qemu_chr_parse_parallel;
|
||||
cc->open = qmp_chardev_open_parallel;
|
||||
#if defined(__linux__)
|
||||
cc->chr_ioctl = pp_ioctl;
|
||||
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
|
||||
defined(__DragonFly__)
|
||||
cc->chr_ioctl = pp_ioctl;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void char_parallel_finalize(Object *obj)
|
||||
{
|
||||
#if defined(__linux__)
|
||||
Chardev *chr = CHARDEV(obj);
|
||||
ParallelChardev *drv = PARALLEL_CHARDEV(chr);
|
||||
int fd = drv->fd;
|
||||
|
||||
pp_hw_mode(drv, IEEE1284_MODE_COMPAT);
|
||||
ioctl(fd, PPRELEASE);
|
||||
close(fd);
|
||||
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
||||
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
|
||||
defined(__DragonFly__)
|
||||
/* FIXME: close fd? */
|
||||
#endif
|
||||
}
|
||||
|
||||
static const TypeInfo char_parallel_type_info = {
|
||||
.name = TYPE_CHARDEV_PARALLEL,
|
||||
.parent = TYPE_CHARDEV,
|
||||
.instance_size = sizeof(ParallelChardev),
|
||||
.instance_finalize = char_parallel_finalize,
|
||||
.class_init = char_parallel_class_init,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&char_parallel_type_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
||||
|
||||
#endif
|
32
chardev/char-parallel.h
Normal file
32
chardev/char-parallel.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef CHAR_PARALLEL_H
|
||||
#define CHAR_PARALLEL_H
|
||||
|
||||
#if defined(__linux__) || defined(__FreeBSD__) || \
|
||||
defined(__FreeBSD_kernel__) || defined(__DragonFly__)
|
||||
#define HAVE_CHARDEV_PARPORT 1
|
||||
#endif
|
||||
|
||||
#endif /* CHAR_PARALLEL_H */
|
191
chardev/char-pipe.c
Normal file
191
chardev/char-pipe.c
Normal file
@ -0,0 +1,191 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "sysemu/char.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "char-win.h"
|
||||
#else
|
||||
#include "char-fd.h"
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#define MAXCONNECT 1
|
||||
#define NTIMEOUT 5000
|
||||
|
||||
static int win_chr_pipe_init(Chardev *chr, const char *filename,
|
||||
Error **errp)
|
||||
{
|
||||
WinChardev *s = WIN_CHARDEV(chr);
|
||||
OVERLAPPED ov;
|
||||
int ret;
|
||||
DWORD size;
|
||||
char *openname;
|
||||
|
||||
s->fpipe = TRUE;
|
||||
|
||||
s->hsend = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
if (!s->hsend) {
|
||||
error_setg(errp, "Failed CreateEvent");
|
||||
goto fail;
|
||||
}
|
||||
s->hrecv = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
if (!s->hrecv) {
|
||||
error_setg(errp, "Failed CreateEvent");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
openname = g_strdup_printf("\\\\.\\pipe\\%s", filename);
|
||||
s->hcom = CreateNamedPipe(openname,
|
||||
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
|
||||
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE |
|
||||
PIPE_WAIT,
|
||||
MAXCONNECT, NSENDBUF, NRECVBUF, NTIMEOUT, NULL);
|
||||
g_free(openname);
|
||||
if (s->hcom == INVALID_HANDLE_VALUE) {
|
||||
error_setg(errp, "Failed CreateNamedPipe (%lu)", GetLastError());
|
||||
s->hcom = NULL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ZeroMemory(&ov, sizeof(ov));
|
||||
ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
ret = ConnectNamedPipe(s->hcom, &ov);
|
||||
if (ret) {
|
||||
error_setg(errp, "Failed ConnectNamedPipe");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = GetOverlappedResult(s->hcom, &ov, &size, TRUE);
|
||||
if (!ret) {
|
||||
error_setg(errp, "Failed GetOverlappedResult");
|
||||
if (ov.hEvent) {
|
||||
CloseHandle(ov.hEvent);
|
||||
ov.hEvent = NULL;
|
||||
}
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (ov.hEvent) {
|
||||
CloseHandle(ov.hEvent);
|
||||
ov.hEvent = NULL;
|
||||
}
|
||||
qemu_add_polling_cb(win_chr_pipe_poll, chr);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void qemu_chr_open_pipe(Chardev *chr,
|
||||
ChardevBackend *backend,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
ChardevHostdev *opts = backend->u.pipe.data;
|
||||
const char *filename = opts->device;
|
||||
|
||||
if (win_chr_pipe_init(chr, filename, errp) < 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void qemu_chr_open_pipe(Chardev *chr,
|
||||
ChardevBackend *backend,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
ChardevHostdev *opts = backend->u.pipe.data;
|
||||
int fd_in, fd_out;
|
||||
char *filename_in;
|
||||
char *filename_out;
|
||||
const char *filename = opts->device;
|
||||
|
||||
filename_in = g_strdup_printf("%s.in", filename);
|
||||
filename_out = g_strdup_printf("%s.out", filename);
|
||||
TFR(fd_in = qemu_open(filename_in, O_RDWR | O_BINARY));
|
||||
TFR(fd_out = qemu_open(filename_out, O_RDWR | O_BINARY));
|
||||
g_free(filename_in);
|
||||
g_free(filename_out);
|
||||
if (fd_in < 0 || fd_out < 0) {
|
||||
if (fd_in >= 0) {
|
||||
close(fd_in);
|
||||
}
|
||||
if (fd_out >= 0) {
|
||||
close(fd_out);
|
||||
}
|
||||
TFR(fd_in = fd_out = qemu_open(filename, O_RDWR | O_BINARY));
|
||||
if (fd_in < 0) {
|
||||
error_setg_file_open(errp, errno, filename);
|
||||
return;
|
||||
}
|
||||
}
|
||||
qemu_chr_open_fd(chr, fd_in, fd_out);
|
||||
}
|
||||
|
||||
#endif /* !_WIN32 */
|
||||
|
||||
static void qemu_chr_parse_pipe(QemuOpts *opts, ChardevBackend *backend,
|
||||
Error **errp)
|
||||
{
|
||||
const char *device = qemu_opt_get(opts, "path");
|
||||
ChardevHostdev *dev;
|
||||
|
||||
if (device == NULL) {
|
||||
error_setg(errp, "chardev: pipe: no device path given");
|
||||
return;
|
||||
}
|
||||
backend->type = CHARDEV_BACKEND_KIND_PIPE;
|
||||
dev = backend->u.pipe.data = g_new0(ChardevHostdev, 1);
|
||||
qemu_chr_parse_common(opts, qapi_ChardevHostdev_base(dev));
|
||||
dev->device = g_strdup(device);
|
||||
}
|
||||
|
||||
static void char_pipe_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->parse = qemu_chr_parse_pipe;
|
||||
cc->open = qemu_chr_open_pipe;
|
||||
}
|
||||
|
||||
static const TypeInfo char_pipe_type_info = {
|
||||
.name = TYPE_CHARDEV_PIPE,
|
||||
#ifdef _WIN32
|
||||
.parent = TYPE_CHARDEV_WIN,
|
||||
#else
|
||||
.parent = TYPE_CHARDEV_FD,
|
||||
#endif
|
||||
.class_init = char_pipe_class_init,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&char_pipe_type_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
300
chardev/char-pty.c
Normal file
300
chardev/char-pty.c
Normal file
@ -0,0 +1,300 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu-common.h"
|
||||
#include "sysemu/char.h"
|
||||
#include "io/channel-file.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "qemu/error-report.h"
|
||||
|
||||
#include "char-io.h"
|
||||
|
||||
#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
|
||||
|| defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
|
||||
|| defined(__GLIBC__)
|
||||
|
||||
typedef struct {
|
||||
Chardev parent;
|
||||
QIOChannel *ioc;
|
||||
int read_bytes;
|
||||
|
||||
/* Protected by the Chardev chr_write_lock. */
|
||||
int connected;
|
||||
guint timer_tag;
|
||||
guint open_tag;
|
||||
} PtyChardev;
|
||||
|
||||
#define PTY_CHARDEV(obj) OBJECT_CHECK(PtyChardev, (obj), TYPE_CHARDEV_PTY)
|
||||
|
||||
static void pty_chr_update_read_handler_locked(Chardev *chr);
|
||||
static void pty_chr_state(Chardev *chr, int connected);
|
||||
|
||||
static gboolean pty_chr_timer(gpointer opaque)
|
||||
{
|
||||
struct Chardev *chr = CHARDEV(opaque);
|
||||
PtyChardev *s = PTY_CHARDEV(opaque);
|
||||
|
||||
qemu_mutex_lock(&chr->chr_write_lock);
|
||||
s->timer_tag = 0;
|
||||
s->open_tag = 0;
|
||||
if (!s->connected) {
|
||||
/* Next poll ... */
|
||||
pty_chr_update_read_handler_locked(chr);
|
||||
}
|
||||
qemu_mutex_unlock(&chr->chr_write_lock);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Called with chr_write_lock held. */
|
||||
static void pty_chr_rearm_timer(Chardev *chr, int ms)
|
||||
{
|
||||
PtyChardev *s = PTY_CHARDEV(chr);
|
||||
char *name;
|
||||
|
||||
if (s->timer_tag) {
|
||||
g_source_remove(s->timer_tag);
|
||||
s->timer_tag = 0;
|
||||
}
|
||||
|
||||
if (ms == 1000) {
|
||||
name = g_strdup_printf("pty-timer-secs-%s", chr->label);
|
||||
s->timer_tag = g_timeout_add_seconds(1, pty_chr_timer, chr);
|
||||
} else {
|
||||
name = g_strdup_printf("pty-timer-ms-%s", chr->label);
|
||||
s->timer_tag = g_timeout_add(ms, pty_chr_timer, chr);
|
||||
}
|
||||
g_source_set_name_by_id(s->timer_tag, name);
|
||||
g_free(name);
|
||||
}
|
||||
|
||||
/* Called with chr_write_lock held. */
|
||||
static void pty_chr_update_read_handler_locked(Chardev *chr)
|
||||
{
|
||||
PtyChardev *s = PTY_CHARDEV(chr);
|
||||
GPollFD pfd;
|
||||
int rc;
|
||||
QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc);
|
||||
|
||||
pfd.fd = fioc->fd;
|
||||
pfd.events = G_IO_OUT;
|
||||
pfd.revents = 0;
|
||||
do {
|
||||
rc = g_poll(&pfd, 1, 0);
|
||||
} while (rc == -1 && errno == EINTR);
|
||||
assert(rc >= 0);
|
||||
|
||||
if (pfd.revents & G_IO_HUP) {
|
||||
pty_chr_state(chr, 0);
|
||||
} else {
|
||||
pty_chr_state(chr, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void pty_chr_update_read_handler(Chardev *chr,
|
||||
GMainContext *context)
|
||||
{
|
||||
qemu_mutex_lock(&chr->chr_write_lock);
|
||||
pty_chr_update_read_handler_locked(chr);
|
||||
qemu_mutex_unlock(&chr->chr_write_lock);
|
||||
}
|
||||
|
||||
/* Called with chr_write_lock held. */
|
||||
static int char_pty_chr_write(Chardev *chr, const uint8_t *buf, int len)
|
||||
{
|
||||
PtyChardev *s = PTY_CHARDEV(chr);
|
||||
|
||||
if (!s->connected) {
|
||||
/* guest sends data, check for (re-)connect */
|
||||
pty_chr_update_read_handler_locked(chr);
|
||||
if (!s->connected) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return io_channel_send(s->ioc, buf, len);
|
||||
}
|
||||
|
||||
static GSource *pty_chr_add_watch(Chardev *chr, GIOCondition cond)
|
||||
{
|
||||
PtyChardev *s = PTY_CHARDEV(chr);
|
||||
if (!s->connected) {
|
||||
return NULL;
|
||||
}
|
||||
return qio_channel_create_watch(s->ioc, cond);
|
||||
}
|
||||
|
||||
static int pty_chr_read_poll(void *opaque)
|
||||
{
|
||||
Chardev *chr = CHARDEV(opaque);
|
||||
PtyChardev *s = PTY_CHARDEV(opaque);
|
||||
|
||||
s->read_bytes = qemu_chr_be_can_write(chr);
|
||||
return s->read_bytes;
|
||||
}
|
||||
|
||||
static gboolean pty_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
|
||||
{
|
||||
Chardev *chr = CHARDEV(opaque);
|
||||
PtyChardev *s = PTY_CHARDEV(opaque);
|
||||
gsize len;
|
||||
uint8_t buf[CHR_READ_BUF_LEN];
|
||||
ssize_t ret;
|
||||
|
||||
len = sizeof(buf);
|
||||
if (len > s->read_bytes) {
|
||||
len = s->read_bytes;
|
||||
}
|
||||
if (len == 0) {
|
||||
return TRUE;
|
||||
}
|
||||
ret = qio_channel_read(s->ioc, (char *)buf, len, NULL);
|
||||
if (ret <= 0) {
|
||||
pty_chr_state(chr, 0);
|
||||
return FALSE;
|
||||
} else {
|
||||
pty_chr_state(chr, 1);
|
||||
qemu_chr_be_write(chr, buf, ret);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean qemu_chr_be_generic_open_func(gpointer opaque)
|
||||
{
|
||||
Chardev *chr = CHARDEV(opaque);
|
||||
PtyChardev *s = PTY_CHARDEV(opaque);
|
||||
|
||||
s->open_tag = 0;
|
||||
qemu_chr_be_generic_open(chr);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Called with chr_write_lock held. */
|
||||
static void pty_chr_state(Chardev *chr, int connected)
|
||||
{
|
||||
PtyChardev *s = PTY_CHARDEV(chr);
|
||||
|
||||
if (!connected) {
|
||||
if (s->open_tag) {
|
||||
g_source_remove(s->open_tag);
|
||||
s->open_tag = 0;
|
||||
}
|
||||
remove_fd_in_watch(chr);
|
||||
s->connected = 0;
|
||||
/* (re-)connect poll interval for idle guests: once per second.
|
||||
* We check more frequently in case the guests sends data to
|
||||
* the virtual device linked to our pty. */
|
||||
pty_chr_rearm_timer(chr, 1000);
|
||||
} else {
|
||||
if (s->timer_tag) {
|
||||
g_source_remove(s->timer_tag);
|
||||
s->timer_tag = 0;
|
||||
}
|
||||
if (!s->connected) {
|
||||
g_assert(s->open_tag == 0);
|
||||
s->connected = 1;
|
||||
s->open_tag = g_idle_add(qemu_chr_be_generic_open_func, chr);
|
||||
}
|
||||
if (!chr->fd_in_tag) {
|
||||
chr->fd_in_tag = io_add_watch_poll(chr, s->ioc,
|
||||
pty_chr_read_poll,
|
||||
pty_chr_read,
|
||||
chr, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void char_pty_finalize(Object *obj)
|
||||
{
|
||||
Chardev *chr = CHARDEV(obj);
|
||||
PtyChardev *s = PTY_CHARDEV(obj);
|
||||
|
||||
qemu_mutex_lock(&chr->chr_write_lock);
|
||||
pty_chr_state(chr, 0);
|
||||
object_unref(OBJECT(s->ioc));
|
||||
if (s->timer_tag) {
|
||||
g_source_remove(s->timer_tag);
|
||||
s->timer_tag = 0;
|
||||
}
|
||||
qemu_mutex_unlock(&chr->chr_write_lock);
|
||||
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
||||
}
|
||||
|
||||
static void char_pty_open(Chardev *chr,
|
||||
ChardevBackend *backend,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
PtyChardev *s;
|
||||
int master_fd, slave_fd;
|
||||
char pty_name[PATH_MAX];
|
||||
char *name;
|
||||
|
||||
master_fd = qemu_openpty_raw(&slave_fd, pty_name);
|
||||
if (master_fd < 0) {
|
||||
error_setg_errno(errp, errno, "Failed to create PTY");
|
||||
return;
|
||||
}
|
||||
|
||||
close(slave_fd);
|
||||
qemu_set_nonblock(master_fd);
|
||||
|
||||
chr->filename = g_strdup_printf("pty:%s", pty_name);
|
||||
error_report("char device redirected to %s (label %s)",
|
||||
pty_name, chr->label);
|
||||
|
||||
s = PTY_CHARDEV(chr);
|
||||
s->ioc = QIO_CHANNEL(qio_channel_file_new_fd(master_fd));
|
||||
name = g_strdup_printf("chardev-pty-%s", chr->label);
|
||||
qio_channel_set_name(QIO_CHANNEL(s->ioc), name);
|
||||
g_free(name);
|
||||
s->timer_tag = 0;
|
||||
*be_opened = false;
|
||||
}
|
||||
|
||||
static void char_pty_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->open = char_pty_open;
|
||||
cc->chr_write = char_pty_chr_write;
|
||||
cc->chr_update_read_handler = pty_chr_update_read_handler;
|
||||
cc->chr_add_watch = pty_chr_add_watch;
|
||||
}
|
||||
|
||||
static const TypeInfo char_pty_type_info = {
|
||||
.name = TYPE_CHARDEV_PTY,
|
||||
.parent = TYPE_CHARDEV,
|
||||
.instance_size = sizeof(PtyChardev),
|
||||
.instance_finalize = char_pty_finalize,
|
||||
.class_init = char_pty_class_init,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&char_pty_type_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
||||
|
||||
#endif
|
249
chardev/char-ringbuf.c
Normal file
249
chardev/char-ringbuf.c
Normal file
@ -0,0 +1,249 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/char.h"
|
||||
#include "qmp-commands.h"
|
||||
#include "qemu/base64.h"
|
||||
|
||||
/* Ring buffer chardev */
|
||||
|
||||
typedef struct {
|
||||
Chardev parent;
|
||||
size_t size;
|
||||
size_t prod;
|
||||
size_t cons;
|
||||
uint8_t *cbuf;
|
||||
} RingBufChardev;
|
||||
|
||||
#define RINGBUF_CHARDEV(obj) \
|
||||
OBJECT_CHECK(RingBufChardev, (obj), TYPE_CHARDEV_RINGBUF)
|
||||
|
||||
static size_t ringbuf_count(const Chardev *chr)
|
||||
{
|
||||
const RingBufChardev *d = RINGBUF_CHARDEV(chr);
|
||||
|
||||
return d->prod - d->cons;
|
||||
}
|
||||
|
||||
static int ringbuf_chr_write(Chardev *chr, const uint8_t *buf, int len)
|
||||
{
|
||||
RingBufChardev *d = RINGBUF_CHARDEV(chr);
|
||||
int i;
|
||||
|
||||
if (!buf || (len < 0)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
d->cbuf[d->prod++ & (d->size - 1)] = buf[i];
|
||||
if (d->prod - d->cons > d->size) {
|
||||
d->cons = d->prod - d->size;
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int ringbuf_chr_read(Chardev *chr, uint8_t *buf, int len)
|
||||
{
|
||||
RingBufChardev *d = RINGBUF_CHARDEV(chr);
|
||||
int i;
|
||||
|
||||
qemu_mutex_lock(&chr->chr_write_lock);
|
||||
for (i = 0; i < len && d->cons != d->prod; i++) {
|
||||
buf[i] = d->cbuf[d->cons++ & (d->size - 1)];
|
||||
}
|
||||
qemu_mutex_unlock(&chr->chr_write_lock);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static void char_ringbuf_finalize(Object *obj)
|
||||
{
|
||||
RingBufChardev *d = RINGBUF_CHARDEV(obj);
|
||||
|
||||
g_free(d->cbuf);
|
||||
}
|
||||
|
||||
static void qemu_chr_open_ringbuf(Chardev *chr,
|
||||
ChardevBackend *backend,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
ChardevRingbuf *opts = backend->u.ringbuf.data;
|
||||
RingBufChardev *d = RINGBUF_CHARDEV(chr);
|
||||
|
||||
d->size = opts->has_size ? opts->size : 65536;
|
||||
|
||||
/* The size must be power of 2 */
|
||||
if (d->size & (d->size - 1)) {
|
||||
error_setg(errp, "size of ringbuf chardev must be power of two");
|
||||
return;
|
||||
}
|
||||
|
||||
d->prod = 0;
|
||||
d->cons = 0;
|
||||
d->cbuf = g_malloc0(d->size);
|
||||
}
|
||||
|
||||
void qmp_ringbuf_write(const char *device, const char *data,
|
||||
bool has_format, enum DataFormat format,
|
||||
Error **errp)
|
||||
{
|
||||
Chardev *chr;
|
||||
const uint8_t *write_data;
|
||||
int ret;
|
||||
gsize write_count;
|
||||
|
||||
chr = qemu_chr_find(device);
|
||||
if (!chr) {
|
||||
error_setg(errp, "Device '%s' not found", device);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CHARDEV_IS_RINGBUF(chr)) {
|
||||
error_setg(errp, "%s is not a ringbuf device", device);
|
||||
return;
|
||||
}
|
||||
|
||||
if (has_format && (format == DATA_FORMAT_BASE64)) {
|
||||
write_data = qbase64_decode(data, -1,
|
||||
&write_count,
|
||||
errp);
|
||||
if (!write_data) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
write_data = (uint8_t *)data;
|
||||
write_count = strlen(data);
|
||||
}
|
||||
|
||||
ret = ringbuf_chr_write(chr, write_data, write_count);
|
||||
|
||||
if (write_data != (uint8_t *)data) {
|
||||
g_free((void *)write_data);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Failed to write to device %s", device);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
char *qmp_ringbuf_read(const char *device, int64_t size,
|
||||
bool has_format, enum DataFormat format,
|
||||
Error **errp)
|
||||
{
|
||||
Chardev *chr;
|
||||
uint8_t *read_data;
|
||||
size_t count;
|
||||
char *data;
|
||||
|
||||
chr = qemu_chr_find(device);
|
||||
if (!chr) {
|
||||
error_setg(errp, "Device '%s' not found", device);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!CHARDEV_IS_RINGBUF(chr)) {
|
||||
error_setg(errp, "%s is not a ringbuf device", device);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (size <= 0) {
|
||||
error_setg(errp, "size must be greater than zero");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
count = ringbuf_count(chr);
|
||||
size = size > count ? count : size;
|
||||
read_data = g_malloc(size + 1);
|
||||
|
||||
ringbuf_chr_read(chr, read_data, size);
|
||||
|
||||
if (has_format && (format == DATA_FORMAT_BASE64)) {
|
||||
data = g_base64_encode(read_data, size);
|
||||
g_free(read_data);
|
||||
} else {
|
||||
/*
|
||||
* FIXME should read only complete, valid UTF-8 characters up
|
||||
* to @size bytes. Invalid sequences should be replaced by a
|
||||
* suitable replacement character. Except when (and only
|
||||
* when) ring buffer lost characters since last read, initial
|
||||
* continuation characters should be dropped.
|
||||
*/
|
||||
read_data[size] = 0;
|
||||
data = (char *)read_data;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend,
|
||||
Error **errp)
|
||||
{
|
||||
int val;
|
||||
ChardevRingbuf *ringbuf;
|
||||
|
||||
backend->type = CHARDEV_BACKEND_KIND_RINGBUF;
|
||||
ringbuf = backend->u.ringbuf.data = g_new0(ChardevRingbuf, 1);
|
||||
qemu_chr_parse_common(opts, qapi_ChardevRingbuf_base(ringbuf));
|
||||
|
||||
val = qemu_opt_get_size(opts, "size", 0);
|
||||
if (val != 0) {
|
||||
ringbuf->has_size = true;
|
||||
ringbuf->size = val;
|
||||
}
|
||||
}
|
||||
|
||||
static void char_ringbuf_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->parse = qemu_chr_parse_ringbuf;
|
||||
cc->open = qemu_chr_open_ringbuf;
|
||||
cc->chr_write = ringbuf_chr_write;
|
||||
}
|
||||
|
||||
static const TypeInfo char_ringbuf_type_info = {
|
||||
.name = TYPE_CHARDEV_RINGBUF,
|
||||
.parent = TYPE_CHARDEV,
|
||||
.class_init = char_ringbuf_class_init,
|
||||
.instance_size = sizeof(RingBufChardev),
|
||||
.instance_finalize = char_ringbuf_finalize,
|
||||
};
|
||||
|
||||
/* Bug-compatibility: */
|
||||
static const TypeInfo char_memory_type_info = {
|
||||
.name = TYPE_CHARDEV_MEMORY,
|
||||
.parent = TYPE_CHARDEV_RINGBUF,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&char_ringbuf_type_info);
|
||||
type_register_static(&char_memory_type_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
318
chardev/char-serial.c
Normal file
318
chardev/char-serial.c
Normal file
@ -0,0 +1,318 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "io/channel-file.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "char-win.h"
|
||||
#else
|
||||
#include <sys/ioctl.h>
|
||||
#include <termios.h>
|
||||
#include "char-fd.h"
|
||||
#endif
|
||||
|
||||
#include "char-serial.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
static void qmp_chardev_open_serial(Chardev *chr,
|
||||
ChardevBackend *backend,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
ChardevHostdev *serial = backend->u.serial.data;
|
||||
|
||||
win_chr_init(chr, serial->device, errp);
|
||||
}
|
||||
|
||||
#elif defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
|
||||
|| defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
|
||||
|| defined(__GLIBC__)
|
||||
|
||||
static void tty_serial_init(int fd, int speed,
|
||||
int parity, int data_bits, int stop_bits)
|
||||
{
|
||||
struct termios tty;
|
||||
speed_t spd;
|
||||
|
||||
#if 0
|
||||
printf("tty_serial_init: speed=%d parity=%c data=%d stop=%d\n",
|
||||
speed, parity, data_bits, stop_bits);
|
||||
#endif
|
||||
tcgetattr(fd, &tty);
|
||||
|
||||
#define check_speed(val) if (speed <= val) { spd = B##val; break; }
|
||||
speed = speed * 10 / 11;
|
||||
do {
|
||||
check_speed(50);
|
||||
check_speed(75);
|
||||
check_speed(110);
|
||||
check_speed(134);
|
||||
check_speed(150);
|
||||
check_speed(200);
|
||||
check_speed(300);
|
||||
check_speed(600);
|
||||
check_speed(1200);
|
||||
check_speed(1800);
|
||||
check_speed(2400);
|
||||
check_speed(4800);
|
||||
check_speed(9600);
|
||||
check_speed(19200);
|
||||
check_speed(38400);
|
||||
/* Non-Posix values follow. They may be unsupported on some systems. */
|
||||
check_speed(57600);
|
||||
check_speed(115200);
|
||||
#ifdef B230400
|
||||
check_speed(230400);
|
||||
#endif
|
||||
#ifdef B460800
|
||||
check_speed(460800);
|
||||
#endif
|
||||
#ifdef B500000
|
||||
check_speed(500000);
|
||||
#endif
|
||||
#ifdef B576000
|
||||
check_speed(576000);
|
||||
#endif
|
||||
#ifdef B921600
|
||||
check_speed(921600);
|
||||
#endif
|
||||
#ifdef B1000000
|
||||
check_speed(1000000);
|
||||
#endif
|
||||
#ifdef B1152000
|
||||
check_speed(1152000);
|
||||
#endif
|
||||
#ifdef B1500000
|
||||
check_speed(1500000);
|
||||
#endif
|
||||
#ifdef B2000000
|
||||
check_speed(2000000);
|
||||
#endif
|
||||
#ifdef B2500000
|
||||
check_speed(2500000);
|
||||
#endif
|
||||
#ifdef B3000000
|
||||
check_speed(3000000);
|
||||
#endif
|
||||
#ifdef B3500000
|
||||
check_speed(3500000);
|
||||
#endif
|
||||
#ifdef B4000000
|
||||
check_speed(4000000);
|
||||
#endif
|
||||
spd = B115200;
|
||||
} while (0);
|
||||
|
||||
cfsetispeed(&tty, spd);
|
||||
cfsetospeed(&tty, spd);
|
||||
|
||||
tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
|
||||
| INLCR | IGNCR | ICRNL | IXON);
|
||||
tty.c_oflag |= OPOST;
|
||||
tty.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG);
|
||||
tty.c_cflag &= ~(CSIZE | PARENB | PARODD | CRTSCTS | CSTOPB);
|
||||
switch (data_bits) {
|
||||
default:
|
||||
case 8:
|
||||
tty.c_cflag |= CS8;
|
||||
break;
|
||||
case 7:
|
||||
tty.c_cflag |= CS7;
|
||||
break;
|
||||
case 6:
|
||||
tty.c_cflag |= CS6;
|
||||
break;
|
||||
case 5:
|
||||
tty.c_cflag |= CS5;
|
||||
break;
|
||||
}
|
||||
switch (parity) {
|
||||
default:
|
||||
case 'N':
|
||||
break;
|
||||
case 'E':
|
||||
tty.c_cflag |= PARENB;
|
||||
break;
|
||||
case 'O':
|
||||
tty.c_cflag |= PARENB | PARODD;
|
||||
break;
|
||||
}
|
||||
if (stop_bits == 2) {
|
||||
tty.c_cflag |= CSTOPB;
|
||||
}
|
||||
|
||||
tcsetattr(fd, TCSANOW, &tty);
|
||||
}
|
||||
|
||||
static int tty_serial_ioctl(Chardev *chr, int cmd, void *arg)
|
||||
{
|
||||
FDChardev *s = FD_CHARDEV(chr);
|
||||
QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc_in);
|
||||
|
||||
switch (cmd) {
|
||||
case CHR_IOCTL_SERIAL_SET_PARAMS:
|
||||
{
|
||||
QEMUSerialSetParams *ssp = arg;
|
||||
tty_serial_init(fioc->fd,
|
||||
ssp->speed, ssp->parity,
|
||||
ssp->data_bits, ssp->stop_bits);
|
||||
}
|
||||
break;
|
||||
case CHR_IOCTL_SERIAL_SET_BREAK:
|
||||
{
|
||||
int enable = *(int *)arg;
|
||||
if (enable) {
|
||||
tcsendbreak(fioc->fd, 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CHR_IOCTL_SERIAL_GET_TIOCM:
|
||||
{
|
||||
int sarg = 0;
|
||||
int *targ = (int *)arg;
|
||||
ioctl(fioc->fd, TIOCMGET, &sarg);
|
||||
*targ = 0;
|
||||
if (sarg & TIOCM_CTS) {
|
||||
*targ |= CHR_TIOCM_CTS;
|
||||
}
|
||||
if (sarg & TIOCM_CAR) {
|
||||
*targ |= CHR_TIOCM_CAR;
|
||||
}
|
||||
if (sarg & TIOCM_DSR) {
|
||||
*targ |= CHR_TIOCM_DSR;
|
||||
}
|
||||
if (sarg & TIOCM_RI) {
|
||||
*targ |= CHR_TIOCM_RI;
|
||||
}
|
||||
if (sarg & TIOCM_DTR) {
|
||||
*targ |= CHR_TIOCM_DTR;
|
||||
}
|
||||
if (sarg & TIOCM_RTS) {
|
||||
*targ |= CHR_TIOCM_RTS;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CHR_IOCTL_SERIAL_SET_TIOCM:
|
||||
{
|
||||
int sarg = *(int *)arg;
|
||||
int targ = 0;
|
||||
ioctl(fioc->fd, TIOCMGET, &targ);
|
||||
targ &= ~(CHR_TIOCM_CTS | CHR_TIOCM_CAR | CHR_TIOCM_DSR
|
||||
| CHR_TIOCM_RI | CHR_TIOCM_DTR | CHR_TIOCM_RTS);
|
||||
if (sarg & CHR_TIOCM_CTS) {
|
||||
targ |= TIOCM_CTS;
|
||||
}
|
||||
if (sarg & CHR_TIOCM_CAR) {
|
||||
targ |= TIOCM_CAR;
|
||||
}
|
||||
if (sarg & CHR_TIOCM_DSR) {
|
||||
targ |= TIOCM_DSR;
|
||||
}
|
||||
if (sarg & CHR_TIOCM_RI) {
|
||||
targ |= TIOCM_RI;
|
||||
}
|
||||
if (sarg & CHR_TIOCM_DTR) {
|
||||
targ |= TIOCM_DTR;
|
||||
}
|
||||
if (sarg & CHR_TIOCM_RTS) {
|
||||
targ |= TIOCM_RTS;
|
||||
}
|
||||
ioctl(fioc->fd, TIOCMSET, &targ);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qmp_chardev_open_serial(Chardev *chr,
|
||||
ChardevBackend *backend,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
ChardevHostdev *serial = backend->u.serial.data;
|
||||
int fd;
|
||||
|
||||
fd = qmp_chardev_open_file_source(serial->device, O_RDWR, errp);
|
||||
if (fd < 0) {
|
||||
return;
|
||||
}
|
||||
qemu_set_nonblock(fd);
|
||||
tty_serial_init(fd, 115200, 'N', 8, 1);
|
||||
|
||||
qemu_chr_open_fd(chr, fd, fd);
|
||||
}
|
||||
#endif /* __linux__ || __sun__ */
|
||||
|
||||
#ifdef HAVE_CHARDEV_SERIAL
|
||||
static void qemu_chr_parse_serial(QemuOpts *opts, ChardevBackend *backend,
|
||||
Error **errp)
|
||||
{
|
||||
const char *device = qemu_opt_get(opts, "path");
|
||||
ChardevHostdev *serial;
|
||||
|
||||
if (device == NULL) {
|
||||
error_setg(errp, "chardev: serial/tty: no device path given");
|
||||
return;
|
||||
}
|
||||
backend->type = CHARDEV_BACKEND_KIND_SERIAL;
|
||||
serial = backend->u.serial.data = g_new0(ChardevHostdev, 1);
|
||||
qemu_chr_parse_common(opts, qapi_ChardevHostdev_base(serial));
|
||||
serial->device = g_strdup(device);
|
||||
}
|
||||
|
||||
static void char_serial_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->parse = qemu_chr_parse_serial;
|
||||
cc->open = qmp_chardev_open_serial;
|
||||
#ifndef _WIN32
|
||||
cc->chr_ioctl = tty_serial_ioctl;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static const TypeInfo char_serial_type_info = {
|
||||
.name = TYPE_CHARDEV_SERIAL,
|
||||
#ifdef _WIN32
|
||||
.parent = TYPE_CHARDEV_WIN,
|
||||
#else
|
||||
.parent = TYPE_CHARDEV_FD,
|
||||
#endif
|
||||
.class_init = char_serial_class_init,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&char_serial_type_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
||||
|
||||
#endif
|
35
chardev/char-serial.h
Normal file
35
chardev/char-serial.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef CHAR_SERIAL_H
|
||||
#define CHAR_SERIAL_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#define HAVE_CHARDEV_SERIAL 1
|
||||
#elif defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
|
||||
|| defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
|
||||
|| defined(__GLIBC__)
|
||||
#define HAVE_CHARDEV_SERIAL 1
|
||||
#endif
|
||||
|
||||
#endif
|
1017
chardev/char-socket.c
Normal file
1017
chardev/char-socket.c
Normal file
File diff suppressed because it is too large
Load Diff
164
chardev/char-stdio.c
Normal file
164
chardev/char-stdio.c
Normal file
@ -0,0 +1,164 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu-common.h"
|
||||
#include "sysemu/char.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "char-win.h"
|
||||
#include "char-win-stdio.h"
|
||||
#else
|
||||
#include <termios.h>
|
||||
#include "char-fd.h"
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
/* init terminal so that we can grab keys */
|
||||
static struct termios oldtty;
|
||||
static int old_fd0_flags;
|
||||
static bool stdio_in_use;
|
||||
static bool stdio_allow_signal;
|
||||
static bool stdio_echo_state;
|
||||
|
||||
static void term_exit(void)
|
||||
{
|
||||
tcsetattr(0, TCSANOW, &oldtty);
|
||||
fcntl(0, F_SETFL, old_fd0_flags);
|
||||
}
|
||||
|
||||
static void qemu_chr_set_echo_stdio(Chardev *chr, bool echo)
|
||||
{
|
||||
struct termios tty;
|
||||
|
||||
stdio_echo_state = echo;
|
||||
tty = oldtty;
|
||||
if (!echo) {
|
||||
tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
|
||||
| INLCR | IGNCR | ICRNL | IXON);
|
||||
tty.c_oflag |= OPOST;
|
||||
tty.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN);
|
||||
tty.c_cflag &= ~(CSIZE | PARENB);
|
||||
tty.c_cflag |= CS8;
|
||||
tty.c_cc[VMIN] = 1;
|
||||
tty.c_cc[VTIME] = 0;
|
||||
}
|
||||
if (!stdio_allow_signal) {
|
||||
tty.c_lflag &= ~ISIG;
|
||||
}
|
||||
|
||||
tcsetattr(0, TCSANOW, &tty);
|
||||
}
|
||||
|
||||
static void term_stdio_handler(int sig)
|
||||
{
|
||||
/* restore echo after resume from suspend. */
|
||||
qemu_chr_set_echo_stdio(NULL, stdio_echo_state);
|
||||
}
|
||||
|
||||
static void qemu_chr_open_stdio(Chardev *chr,
|
||||
ChardevBackend *backend,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
ChardevStdio *opts = backend->u.stdio.data;
|
||||
struct sigaction act;
|
||||
|
||||
if (is_daemonized()) {
|
||||
error_setg(errp, "cannot use stdio with -daemonize");
|
||||
return;
|
||||
}
|
||||
|
||||
if (stdio_in_use) {
|
||||
error_setg(errp, "cannot use stdio by multiple character devices");
|
||||
return;
|
||||
}
|
||||
|
||||
stdio_in_use = true;
|
||||
old_fd0_flags = fcntl(0, F_GETFL);
|
||||
tcgetattr(0, &oldtty);
|
||||
qemu_set_nonblock(0);
|
||||
atexit(term_exit);
|
||||
|
||||
memset(&act, 0, sizeof(act));
|
||||
act.sa_handler = term_stdio_handler;
|
||||
sigaction(SIGCONT, &act, NULL);
|
||||
|
||||
qemu_chr_open_fd(chr, 0, 1);
|
||||
|
||||
if (opts->has_signal) {
|
||||
stdio_allow_signal = opts->signal;
|
||||
}
|
||||
qemu_chr_set_echo_stdio(chr, false);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void qemu_chr_parse_stdio(QemuOpts *opts, ChardevBackend *backend,
|
||||
Error **errp)
|
||||
{
|
||||
ChardevStdio *stdio;
|
||||
|
||||
backend->type = CHARDEV_BACKEND_KIND_STDIO;
|
||||
stdio = backend->u.stdio.data = g_new0(ChardevStdio, 1);
|
||||
qemu_chr_parse_common(opts, qapi_ChardevStdio_base(stdio));
|
||||
stdio->has_signal = true;
|
||||
stdio->signal = qemu_opt_get_bool(opts, "signal", true);
|
||||
}
|
||||
|
||||
static void char_stdio_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->parse = qemu_chr_parse_stdio;
|
||||
#ifndef _WIN32
|
||||
cc->open = qemu_chr_open_stdio;
|
||||
cc->chr_set_echo = qemu_chr_set_echo_stdio;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void char_stdio_finalize(Object *obj)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
term_exit();
|
||||
#endif
|
||||
}
|
||||
|
||||
static const TypeInfo char_stdio_type_info = {
|
||||
.name = TYPE_CHARDEV_STDIO,
|
||||
#ifdef _WIN32
|
||||
.parent = TYPE_CHARDEV_WIN_STDIO,
|
||||
#else
|
||||
.parent = TYPE_CHARDEV_FD,
|
||||
#endif
|
||||
.instance_finalize = char_stdio_finalize,
|
||||
.class_init = char_stdio_class_init,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&char_stdio_type_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
233
chardev/char-udp.c
Normal file
233
chardev/char-udp.c
Normal file
@ -0,0 +1,233 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/char.h"
|
||||
#include "io/channel-socket.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
#include "char-io.h"
|
||||
|
||||
/***********************************************************/
|
||||
/* UDP Net console */
|
||||
|
||||
typedef struct {
|
||||
Chardev parent;
|
||||
QIOChannel *ioc;
|
||||
uint8_t buf[CHR_READ_BUF_LEN];
|
||||
int bufcnt;
|
||||
int bufptr;
|
||||
int max_size;
|
||||
} UdpChardev;
|
||||
|
||||
#define UDP_CHARDEV(obj) OBJECT_CHECK(UdpChardev, (obj), TYPE_CHARDEV_UDP)
|
||||
|
||||
/* Called with chr_write_lock held. */
|
||||
static int udp_chr_write(Chardev *chr, const uint8_t *buf, int len)
|
||||
{
|
||||
UdpChardev *s = UDP_CHARDEV(chr);
|
||||
|
||||
return qio_channel_write(
|
||||
s->ioc, (const char *)buf, len, NULL);
|
||||
}
|
||||
|
||||
static int udp_chr_read_poll(void *opaque)
|
||||
{
|
||||
Chardev *chr = CHARDEV(opaque);
|
||||
UdpChardev *s = UDP_CHARDEV(opaque);
|
||||
|
||||
s->max_size = qemu_chr_be_can_write(chr);
|
||||
|
||||
/* If there were any stray characters in the queue process them
|
||||
* first
|
||||
*/
|
||||
while (s->max_size > 0 && s->bufptr < s->bufcnt) {
|
||||
qemu_chr_be_write(chr, &s->buf[s->bufptr], 1);
|
||||
s->bufptr++;
|
||||
s->max_size = qemu_chr_be_can_write(chr);
|
||||
}
|
||||
return s->max_size;
|
||||
}
|
||||
|
||||
static gboolean udp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
|
||||
{
|
||||
Chardev *chr = CHARDEV(opaque);
|
||||
UdpChardev *s = UDP_CHARDEV(opaque);
|
||||
ssize_t ret;
|
||||
|
||||
if (s->max_size == 0) {
|
||||
return TRUE;
|
||||
}
|
||||
ret = qio_channel_read(
|
||||
s->ioc, (char *)s->buf, sizeof(s->buf), NULL);
|
||||
if (ret <= 0) {
|
||||
remove_fd_in_watch(chr);
|
||||
return FALSE;
|
||||
}
|
||||
s->bufcnt = ret;
|
||||
|
||||
s->bufptr = 0;
|
||||
while (s->max_size > 0 && s->bufptr < s->bufcnt) {
|
||||
qemu_chr_be_write(chr, &s->buf[s->bufptr], 1);
|
||||
s->bufptr++;
|
||||
s->max_size = qemu_chr_be_can_write(chr);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void udp_chr_update_read_handler(Chardev *chr,
|
||||
GMainContext *context)
|
||||
{
|
||||
UdpChardev *s = UDP_CHARDEV(chr);
|
||||
|
||||
remove_fd_in_watch(chr);
|
||||
if (s->ioc) {
|
||||
chr->fd_in_tag = io_add_watch_poll(chr, s->ioc,
|
||||
udp_chr_read_poll,
|
||||
udp_chr_read, chr,
|
||||
context);
|
||||
}
|
||||
}
|
||||
|
||||
static void char_udp_finalize(Object *obj)
|
||||
{
|
||||
Chardev *chr = CHARDEV(obj);
|
||||
UdpChardev *s = UDP_CHARDEV(obj);
|
||||
|
||||
remove_fd_in_watch(chr);
|
||||
if (s->ioc) {
|
||||
object_unref(OBJECT(s->ioc));
|
||||
}
|
||||
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
||||
}
|
||||
|
||||
static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
|
||||
Error **errp)
|
||||
{
|
||||
const char *host = qemu_opt_get(opts, "host");
|
||||
const char *port = qemu_opt_get(opts, "port");
|
||||
const char *localaddr = qemu_opt_get(opts, "localaddr");
|
||||
const char *localport = qemu_opt_get(opts, "localport");
|
||||
bool has_local = false;
|
||||
SocketAddress *addr;
|
||||
ChardevUdp *udp;
|
||||
|
||||
backend->type = CHARDEV_BACKEND_KIND_UDP;
|
||||
if (host == NULL || strlen(host) == 0) {
|
||||
host = "localhost";
|
||||
}
|
||||
if (port == NULL || strlen(port) == 0) {
|
||||
error_setg(errp, "chardev: udp: remote port not specified");
|
||||
return;
|
||||
}
|
||||
if (localport == NULL || strlen(localport) == 0) {
|
||||
localport = "0";
|
||||
} else {
|
||||
has_local = true;
|
||||
}
|
||||
if (localaddr == NULL || strlen(localaddr) == 0) {
|
||||
localaddr = "";
|
||||
} else {
|
||||
has_local = true;
|
||||
}
|
||||
|
||||
udp = backend->u.udp.data = g_new0(ChardevUdp, 1);
|
||||
qemu_chr_parse_common(opts, qapi_ChardevUdp_base(udp));
|
||||
|
||||
addr = g_new0(SocketAddress, 1);
|
||||
addr->type = SOCKET_ADDRESS_KIND_INET;
|
||||
addr->u.inet.data = g_new(InetSocketAddress, 1);
|
||||
*addr->u.inet.data = (InetSocketAddress) {
|
||||
.host = g_strdup(host),
|
||||
.port = g_strdup(port),
|
||||
.has_ipv4 = qemu_opt_get(opts, "ipv4"),
|
||||
.ipv4 = qemu_opt_get_bool(opts, "ipv4", 0),
|
||||
.has_ipv6 = qemu_opt_get(opts, "ipv6"),
|
||||
.ipv6 = qemu_opt_get_bool(opts, "ipv6", 0),
|
||||
};
|
||||
udp->remote = addr;
|
||||
|
||||
if (has_local) {
|
||||
udp->has_local = true;
|
||||
addr = g_new0(SocketAddress, 1);
|
||||
addr->type = SOCKET_ADDRESS_KIND_INET;
|
||||
addr->u.inet.data = g_new(InetSocketAddress, 1);
|
||||
*addr->u.inet.data = (InetSocketAddress) {
|
||||
.host = g_strdup(localaddr),
|
||||
.port = g_strdup(localport),
|
||||
};
|
||||
udp->local = addr;
|
||||
}
|
||||
}
|
||||
|
||||
static void qmp_chardev_open_udp(Chardev *chr,
|
||||
ChardevBackend *backend,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
ChardevUdp *udp = backend->u.udp.data;
|
||||
QIOChannelSocket *sioc = qio_channel_socket_new();
|
||||
char *name;
|
||||
UdpChardev *s = UDP_CHARDEV(chr);
|
||||
|
||||
if (qio_channel_socket_dgram_sync(sioc,
|
||||
udp->local, udp->remote,
|
||||
errp) < 0) {
|
||||
object_unref(OBJECT(sioc));
|
||||
return;
|
||||
}
|
||||
|
||||
name = g_strdup_printf("chardev-udp-%s", chr->label);
|
||||
qio_channel_set_name(QIO_CHANNEL(sioc), name);
|
||||
g_free(name);
|
||||
|
||||
s->ioc = QIO_CHANNEL(sioc);
|
||||
/* be isn't opened until we get a connection */
|
||||
*be_opened = false;
|
||||
}
|
||||
|
||||
static void char_udp_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->parse = qemu_chr_parse_udp;
|
||||
cc->open = qmp_chardev_open_udp;
|
||||
cc->chr_write = udp_chr_write;
|
||||
cc->chr_update_read_handler = udp_chr_update_read_handler;
|
||||
}
|
||||
|
||||
static const TypeInfo char_udp_type_info = {
|
||||
.name = TYPE_CHARDEV_UDP,
|
||||
.parent = TYPE_CHARDEV,
|
||||
.instance_size = sizeof(UdpChardev),
|
||||
.instance_finalize = char_udp_finalize,
|
||||
.class_init = char_udp_class_init,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&char_udp_type_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
266
chardev/char-win-stdio.c
Normal file
266
chardev/char-win-stdio.c
Normal file
@ -0,0 +1,266 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "char-win.h"
|
||||
#include "char-win-stdio.h"
|
||||
|
||||
typedef struct {
|
||||
Chardev parent;
|
||||
HANDLE hStdIn;
|
||||
HANDLE hInputReadyEvent;
|
||||
HANDLE hInputDoneEvent;
|
||||
HANDLE hInputThread;
|
||||
uint8_t win_stdio_buf;
|
||||
} WinStdioChardev;
|
||||
|
||||
#define WIN_STDIO_CHARDEV(obj) \
|
||||
OBJECT_CHECK(WinStdioChardev, (obj), TYPE_CHARDEV_WIN_STDIO)
|
||||
|
||||
static void win_stdio_wait_func(void *opaque)
|
||||
{
|
||||
Chardev *chr = CHARDEV(opaque);
|
||||
WinStdioChardev *stdio = WIN_STDIO_CHARDEV(opaque);
|
||||
INPUT_RECORD buf[4];
|
||||
int ret;
|
||||
DWORD dwSize;
|
||||
int i;
|
||||
|
||||
ret = ReadConsoleInput(stdio->hStdIn, buf, ARRAY_SIZE(buf), &dwSize);
|
||||
|
||||
if (!ret) {
|
||||
/* Avoid error storm */
|
||||
qemu_del_wait_object(stdio->hStdIn, NULL, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < dwSize; i++) {
|
||||
KEY_EVENT_RECORD *kev = &buf[i].Event.KeyEvent;
|
||||
|
||||
if (buf[i].EventType == KEY_EVENT && kev->bKeyDown) {
|
||||
int j;
|
||||
if (kev->uChar.AsciiChar != 0) {
|
||||
for (j = 0; j < kev->wRepeatCount; j++) {
|
||||
if (qemu_chr_be_can_write(chr)) {
|
||||
uint8_t c = kev->uChar.AsciiChar;
|
||||
qemu_chr_be_write(chr, &c, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static DWORD WINAPI win_stdio_thread(LPVOID param)
|
||||
{
|
||||
WinStdioChardev *stdio = WIN_STDIO_CHARDEV(param);
|
||||
int ret;
|
||||
DWORD dwSize;
|
||||
|
||||
while (1) {
|
||||
|
||||
/* Wait for one byte */
|
||||
ret = ReadFile(stdio->hStdIn, &stdio->win_stdio_buf, 1, &dwSize, NULL);
|
||||
|
||||
/* Exit in case of error, continue if nothing read */
|
||||
if (!ret) {
|
||||
break;
|
||||
}
|
||||
if (!dwSize) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Some terminal emulator returns \r\n for Enter, just pass \n */
|
||||
if (stdio->win_stdio_buf == '\r') {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Signal the main thread and wait until the byte was eaten */
|
||||
if (!SetEvent(stdio->hInputReadyEvent)) {
|
||||
break;
|
||||
}
|
||||
if (WaitForSingleObject(stdio->hInputDoneEvent, INFINITE)
|
||||
!= WAIT_OBJECT_0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
qemu_del_wait_object(stdio->hInputReadyEvent, NULL, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void win_stdio_thread_wait_func(void *opaque)
|
||||
{
|
||||
Chardev *chr = CHARDEV(opaque);
|
||||
WinStdioChardev *stdio = WIN_STDIO_CHARDEV(opaque);
|
||||
|
||||
if (qemu_chr_be_can_write(chr)) {
|
||||
qemu_chr_be_write(chr, &stdio->win_stdio_buf, 1);
|
||||
}
|
||||
|
||||
SetEvent(stdio->hInputDoneEvent);
|
||||
}
|
||||
|
||||
static void qemu_chr_set_echo_win_stdio(Chardev *chr, bool echo)
|
||||
{
|
||||
WinStdioChardev *stdio = WIN_STDIO_CHARDEV(chr);
|
||||
DWORD dwMode = 0;
|
||||
|
||||
GetConsoleMode(stdio->hStdIn, &dwMode);
|
||||
|
||||
if (echo) {
|
||||
SetConsoleMode(stdio->hStdIn, dwMode | ENABLE_ECHO_INPUT);
|
||||
} else {
|
||||
SetConsoleMode(stdio->hStdIn, dwMode & ~ENABLE_ECHO_INPUT);
|
||||
}
|
||||
}
|
||||
|
||||
static void qemu_chr_open_stdio(Chardev *chr,
|
||||
ChardevBackend *backend,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
WinStdioChardev *stdio = WIN_STDIO_CHARDEV(chr);
|
||||
DWORD dwMode;
|
||||
int is_console = 0;
|
||||
|
||||
stdio->hStdIn = GetStdHandle(STD_INPUT_HANDLE);
|
||||
if (stdio->hStdIn == INVALID_HANDLE_VALUE) {
|
||||
error_setg(errp, "cannot open stdio: invalid handle");
|
||||
return;
|
||||
}
|
||||
|
||||
is_console = GetConsoleMode(stdio->hStdIn, &dwMode) != 0;
|
||||
|
||||
if (is_console) {
|
||||
if (qemu_add_wait_object(stdio->hStdIn,
|
||||
win_stdio_wait_func, chr)) {
|
||||
error_setg(errp, "qemu_add_wait_object: failed");
|
||||
goto err1;
|
||||
}
|
||||
} else {
|
||||
DWORD dwId;
|
||||
|
||||
stdio->hInputReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
stdio->hInputDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
if (stdio->hInputReadyEvent == INVALID_HANDLE_VALUE
|
||||
|| stdio->hInputDoneEvent == INVALID_HANDLE_VALUE) {
|
||||
error_setg(errp, "cannot create event");
|
||||
goto err2;
|
||||
}
|
||||
if (qemu_add_wait_object(stdio->hInputReadyEvent,
|
||||
win_stdio_thread_wait_func, chr)) {
|
||||
error_setg(errp, "qemu_add_wait_object: failed");
|
||||
goto err2;
|
||||
}
|
||||
stdio->hInputThread = CreateThread(NULL, 0, win_stdio_thread,
|
||||
chr, 0, &dwId);
|
||||
|
||||
if (stdio->hInputThread == INVALID_HANDLE_VALUE) {
|
||||
error_setg(errp, "cannot create stdio thread");
|
||||
goto err3;
|
||||
}
|
||||
}
|
||||
|
||||
dwMode |= ENABLE_LINE_INPUT;
|
||||
|
||||
if (is_console) {
|
||||
/* set the terminal in raw mode */
|
||||
/* ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS */
|
||||
dwMode |= ENABLE_PROCESSED_INPUT;
|
||||
}
|
||||
|
||||
SetConsoleMode(stdio->hStdIn, dwMode);
|
||||
|
||||
qemu_chr_set_echo_win_stdio(chr, false);
|
||||
|
||||
return;
|
||||
|
||||
err3:
|
||||
qemu_del_wait_object(stdio->hInputReadyEvent, NULL, NULL);
|
||||
err2:
|
||||
CloseHandle(stdio->hInputReadyEvent);
|
||||
CloseHandle(stdio->hInputDoneEvent);
|
||||
err1:
|
||||
qemu_del_wait_object(stdio->hStdIn, NULL, NULL);
|
||||
}
|
||||
|
||||
static void char_win_stdio_finalize(Object *obj)
|
||||
{
|
||||
WinStdioChardev *stdio = WIN_STDIO_CHARDEV(obj);
|
||||
|
||||
if (stdio->hInputReadyEvent != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(stdio->hInputReadyEvent);
|
||||
}
|
||||
if (stdio->hInputDoneEvent != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(stdio->hInputDoneEvent);
|
||||
}
|
||||
if (stdio->hInputThread != INVALID_HANDLE_VALUE) {
|
||||
TerminateThread(stdio->hInputThread, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int win_stdio_write(Chardev *chr, const uint8_t *buf, int len)
|
||||
{
|
||||
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
DWORD dwSize;
|
||||
int len1;
|
||||
|
||||
len1 = len;
|
||||
|
||||
while (len1 > 0) {
|
||||
if (!WriteFile(hStdOut, buf, len1, &dwSize, NULL)) {
|
||||
break;
|
||||
}
|
||||
buf += dwSize;
|
||||
len1 -= dwSize;
|
||||
}
|
||||
|
||||
return len - len1;
|
||||
}
|
||||
|
||||
static void char_win_stdio_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->open = qemu_chr_open_stdio;
|
||||
cc->chr_write = win_stdio_write;
|
||||
cc->chr_set_echo = qemu_chr_set_echo_win_stdio;
|
||||
}
|
||||
|
||||
static const TypeInfo char_win_stdio_type_info = {
|
||||
.name = TYPE_CHARDEV_WIN_STDIO,
|
||||
.parent = TYPE_CHARDEV,
|
||||
.instance_size = sizeof(WinStdioChardev),
|
||||
.instance_finalize = char_win_stdio_finalize,
|
||||
.class_init = char_win_stdio_class_init,
|
||||
.abstract = true,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&char_win_stdio_type_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
29
chardev/char-win-stdio.h
Normal file
29
chardev/char-win-stdio.h
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef CHAR_WIN_STDIO_H
|
||||
#define CHAR_WIN_STDIO_H
|
||||
|
||||
#define TYPE_CHARDEV_WIN_STDIO "chardev-win-stdio"
|
||||
|
||||
#endif /* CHAR_WIN_STDIO_H */
|
265
chardev/char-win.c
Normal file
265
chardev/char-win.c
Normal file
@ -0,0 +1,265 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qapi/error.h"
|
||||
#include "char-win.h"
|
||||
|
||||
static void win_chr_readfile(Chardev *chr)
|
||||
{
|
||||
WinChardev *s = WIN_CHARDEV(chr);
|
||||
|
||||
int ret, err;
|
||||
uint8_t buf[CHR_READ_BUF_LEN];
|
||||
DWORD size;
|
||||
|
||||
ZeroMemory(&s->orecv, sizeof(s->orecv));
|
||||
s->orecv.hEvent = s->hrecv;
|
||||
ret = ReadFile(s->hcom, buf, s->len, &size, &s->orecv);
|
||||
if (!ret) {
|
||||
err = GetLastError();
|
||||
if (err == ERROR_IO_PENDING) {
|
||||
ret = GetOverlappedResult(s->hcom, &s->orecv, &size, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
if (size > 0) {
|
||||
qemu_chr_be_write(chr, buf, size);
|
||||
}
|
||||
}
|
||||
|
||||
static void win_chr_read(Chardev *chr)
|
||||
{
|
||||
WinChardev *s = WIN_CHARDEV(chr);
|
||||
|
||||
if (s->len > s->max_size) {
|
||||
s->len = s->max_size;
|
||||
}
|
||||
if (s->len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
win_chr_readfile(chr);
|
||||
}
|
||||
|
||||
static int win_chr_read_poll(Chardev *chr)
|
||||
{
|
||||
WinChardev *s = WIN_CHARDEV(chr);
|
||||
|
||||
s->max_size = qemu_chr_be_can_write(chr);
|
||||
return s->max_size;
|
||||
}
|
||||
|
||||
static int win_chr_poll(void *opaque)
|
||||
{
|
||||
Chardev *chr = CHARDEV(opaque);
|
||||
WinChardev *s = WIN_CHARDEV(opaque);
|
||||
COMSTAT status;
|
||||
DWORD comerr;
|
||||
|
||||
ClearCommError(s->hcom, &comerr, &status);
|
||||
if (status.cbInQue > 0) {
|
||||
s->len = status.cbInQue;
|
||||
win_chr_read_poll(chr);
|
||||
win_chr_read(chr);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int win_chr_init(Chardev *chr, const char *filename, Error **errp)
|
||||
{
|
||||
WinChardev *s = WIN_CHARDEV(chr);
|
||||
COMMCONFIG comcfg;
|
||||
COMMTIMEOUTS cto = { 0, 0, 0, 0, 0};
|
||||
COMSTAT comstat;
|
||||
DWORD size;
|
||||
DWORD err;
|
||||
|
||||
s->hsend = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
if (!s->hsend) {
|
||||
error_setg(errp, "Failed CreateEvent");
|
||||
goto fail;
|
||||
}
|
||||
s->hrecv = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
if (!s->hrecv) {
|
||||
error_setg(errp, "Failed CreateEvent");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
s->hcom = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
|
||||
OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
|
||||
if (s->hcom == INVALID_HANDLE_VALUE) {
|
||||
error_setg(errp, "Failed CreateFile (%lu)", GetLastError());
|
||||
s->hcom = NULL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!SetupComm(s->hcom, NRECVBUF, NSENDBUF)) {
|
||||
error_setg(errp, "Failed SetupComm");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ZeroMemory(&comcfg, sizeof(COMMCONFIG));
|
||||
size = sizeof(COMMCONFIG);
|
||||
GetDefaultCommConfig(filename, &comcfg, &size);
|
||||
comcfg.dcb.DCBlength = sizeof(DCB);
|
||||
CommConfigDialog(filename, NULL, &comcfg);
|
||||
|
||||
if (!SetCommState(s->hcom, &comcfg.dcb)) {
|
||||
error_setg(errp, "Failed SetCommState");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!SetCommMask(s->hcom, EV_ERR)) {
|
||||
error_setg(errp, "Failed SetCommMask");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
cto.ReadIntervalTimeout = MAXDWORD;
|
||||
if (!SetCommTimeouts(s->hcom, &cto)) {
|
||||
error_setg(errp, "Failed SetCommTimeouts");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!ClearCommError(s->hcom, &err, &comstat)) {
|
||||
error_setg(errp, "Failed ClearCommError");
|
||||
goto fail;
|
||||
}
|
||||
qemu_add_polling_cb(win_chr_poll, chr);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
int win_chr_pipe_poll(void *opaque)
|
||||
{
|
||||
Chardev *chr = CHARDEV(opaque);
|
||||
WinChardev *s = WIN_CHARDEV(opaque);
|
||||
DWORD size;
|
||||
|
||||
PeekNamedPipe(s->hcom, NULL, 0, NULL, &size, NULL);
|
||||
if (size > 0) {
|
||||
s->len = size;
|
||||
win_chr_read_poll(chr);
|
||||
win_chr_read(chr);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called with chr_write_lock held. */
|
||||
static int win_chr_write(Chardev *chr, const uint8_t *buf, int len1)
|
||||
{
|
||||
WinChardev *s = WIN_CHARDEV(chr);
|
||||
DWORD len, ret, size, err;
|
||||
|
||||
len = len1;
|
||||
ZeroMemory(&s->osend, sizeof(s->osend));
|
||||
s->osend.hEvent = s->hsend;
|
||||
while (len > 0) {
|
||||
if (s->hsend) {
|
||||
ret = WriteFile(s->hcom, buf, len, &size, &s->osend);
|
||||
} else {
|
||||
ret = WriteFile(s->hcom, buf, len, &size, NULL);
|
||||
}
|
||||
if (!ret) {
|
||||
err = GetLastError();
|
||||
if (err == ERROR_IO_PENDING) {
|
||||
ret = GetOverlappedResult(s->hcom, &s->osend, &size, TRUE);
|
||||
if (ret) {
|
||||
buf += size;
|
||||
len -= size;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
buf += size;
|
||||
len -= size;
|
||||
}
|
||||
}
|
||||
return len1 - len;
|
||||
}
|
||||
|
||||
static void char_win_finalize(Object *obj)
|
||||
{
|
||||
Chardev *chr = CHARDEV(obj);
|
||||
WinChardev *s = WIN_CHARDEV(chr);
|
||||
|
||||
if (s->skip_free) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->hsend) {
|
||||
CloseHandle(s->hsend);
|
||||
}
|
||||
if (s->hrecv) {
|
||||
CloseHandle(s->hrecv);
|
||||
}
|
||||
if (s->hcom) {
|
||||
CloseHandle(s->hcom);
|
||||
}
|
||||
if (s->fpipe) {
|
||||
qemu_del_polling_cb(win_chr_pipe_poll, chr);
|
||||
} else {
|
||||
qemu_del_polling_cb(win_chr_poll, chr);
|
||||
}
|
||||
|
||||
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
||||
}
|
||||
|
||||
void qemu_chr_open_win_file(Chardev *chr, HANDLE fd_out)
|
||||
{
|
||||
WinChardev *s = WIN_CHARDEV(chr);
|
||||
|
||||
s->skip_free = true;
|
||||
s->hcom = fd_out;
|
||||
}
|
||||
|
||||
static void char_win_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->chr_write = win_chr_write;
|
||||
}
|
||||
|
||||
static const TypeInfo char_win_type_info = {
|
||||
.name = TYPE_CHARDEV_WIN,
|
||||
.parent = TYPE_CHARDEV,
|
||||
.instance_size = sizeof(WinChardev),
|
||||
.instance_finalize = char_win_finalize,
|
||||
.class_init = char_win_class_init,
|
||||
.abstract = true,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&char_win_type_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
53
chardev/char-win.h
Normal file
53
chardev/char-win.h
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef CHAR_WIN_H
|
||||
#define CHAR_WIN_H
|
||||
|
||||
#include "sysemu/char.h"
|
||||
|
||||
typedef struct {
|
||||
Chardev parent;
|
||||
int max_size;
|
||||
HANDLE hcom, hrecv, hsend;
|
||||
OVERLAPPED orecv;
|
||||
BOOL fpipe;
|
||||
DWORD len;
|
||||
|
||||
/* Protected by the Chardev chr_write_lock. */
|
||||
OVERLAPPED osend;
|
||||
/* FIXME: file/console do not finalize */
|
||||
bool skip_free;
|
||||
} WinChardev;
|
||||
|
||||
#define NSENDBUF 2048
|
||||
#define NRECVBUF 2048
|
||||
|
||||
#define TYPE_CHARDEV_WIN "chardev-win"
|
||||
#define WIN_CHARDEV(obj) OBJECT_CHECK(WinChardev, (obj), TYPE_CHARDEV_WIN)
|
||||
|
||||
void qemu_chr_open_win_file(Chardev *chr, HANDLE fd_out);
|
||||
int win_chr_init(Chardev *chr, const char *filename, Error **errp);
|
||||
int win_chr_pipe_poll(void *opaque);
|
||||
|
||||
#endif /* CHAR_WIN_H */
|
1334
chardev/char.c
Normal file
1334
chardev/char.c
Normal file
File diff suppressed because it is too large
Load Diff
1
hmp.c
1
hmp.c
@ -19,6 +19,7 @@
|
||||
#include "net/eth.h"
|
||||
#include "sysemu/char.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qmp-commands.h"
|
||||
|
@ -2,12 +2,7 @@
|
||||
#define QEMU_CHAR_H
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/queue.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "block/aio.h"
|
||||
#include "qapi/qmp/qobject.h"
|
||||
#include "qapi/qmp/qstring.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/bitmap.h"
|
||||
#include "qom/object.h"
|
||||
@ -22,6 +17,7 @@ typedef enum {
|
||||
CHR_EVENT_CLOSED /* connection closed */
|
||||
} QEMUChrEvent;
|
||||
|
||||
#define CHR_READ_BUF_LEN 4096
|
||||
|
||||
#define CHR_IOCTL_SERIAL_SET_PARAMS 1
|
||||
typedef struct {
|
||||
@ -74,7 +70,7 @@ typedef enum {
|
||||
QEMU_CHAR_FEATURE_REPLAY,
|
||||
|
||||
QEMU_CHAR_FEATURE_LAST,
|
||||
} CharDriverFeature;
|
||||
} ChardevFeature;
|
||||
|
||||
/* This is the backend as seen by frontend, the actual backend is
|
||||
* Chardev */
|
||||
@ -88,8 +84,6 @@ typedef struct CharBackend {
|
||||
int fe_open;
|
||||
} CharBackend;
|
||||
|
||||
typedef struct CharDriver CharDriver;
|
||||
|
||||
struct Chardev {
|
||||
Object parent_obj;
|
||||
|
||||
@ -143,7 +137,7 @@ Chardev *qemu_chr_new(const char *label, const char *filename);
|
||||
* @qemu_chr_fe_disconnect:
|
||||
*
|
||||
* Close a fd accpeted by character backend.
|
||||
* Without associated CharDriver, do nothing.
|
||||
* Without associated Chardev, do nothing.
|
||||
*/
|
||||
void qemu_chr_fe_disconnect(CharBackend *be);
|
||||
|
||||
@ -158,7 +152,7 @@ void qemu_chr_cleanup(void);
|
||||
* @qemu_chr_fe_wait_connected:
|
||||
*
|
||||
* Wait for characted backend to be connected, return < 0 on error or
|
||||
* if no assicated CharDriver.
|
||||
* if no assicated Chardev.
|
||||
*/
|
||||
int qemu_chr_fe_wait_connected(CharBackend *be, Error **errp);
|
||||
|
||||
@ -184,20 +178,13 @@ Chardev *qemu_chr_new_noreplay(const char *label, const char *filename);
|
||||
*/
|
||||
void qemu_chr_delete(Chardev *chr);
|
||||
|
||||
/**
|
||||
* @qemu_chr_free:
|
||||
*
|
||||
* Destroy a character backend.
|
||||
*/
|
||||
void qemu_chr_free(Chardev *chr);
|
||||
|
||||
/**
|
||||
* @qemu_chr_fe_set_echo:
|
||||
*
|
||||
* Ask the backend to override its normal echo setting. This only really
|
||||
* applies to the stdio backend and is used by the QMP server such that you
|
||||
* can see what you type if you try to type QMP commands.
|
||||
* Without associated CharDriver, do nothing.
|
||||
* Without associated Chardev, do nothing.
|
||||
*
|
||||
* @echo true to enable echo, false to disable echo
|
||||
*/
|
||||
@ -208,7 +195,7 @@ void qemu_chr_fe_set_echo(CharBackend *be, bool echo);
|
||||
*
|
||||
* Set character frontend open status. This is an indication that the
|
||||
* front end is ready (or not) to begin doing I/O.
|
||||
* Without associated CharDriver, do nothing.
|
||||
* Without associated Chardev, do nothing.
|
||||
*/
|
||||
void qemu_chr_fe_set_open(CharBackend *be, int fe_open);
|
||||
|
||||
@ -217,7 +204,7 @@ void qemu_chr_fe_set_open(CharBackend *be, int fe_open);
|
||||
*
|
||||
* Write to a character backend using a printf style interface. This
|
||||
* function is thread-safe. It does nothing without associated
|
||||
* CharDriver.
|
||||
* Chardev.
|
||||
*
|
||||
* @fmt see #printf
|
||||
*/
|
||||
@ -230,7 +217,7 @@ void qemu_chr_fe_printf(CharBackend *be, const char *fmt, ...)
|
||||
* If the backend is connected, create and add a #GSource that fires
|
||||
* when the given condition (typically G_IO_OUT|G_IO_HUP or G_IO_HUP)
|
||||
* is active; return the #GSource's tag. If it is disconnected,
|
||||
* or without associated CharDriver, return 0.
|
||||
* or without associated Chardev, return 0.
|
||||
*
|
||||
* @cond the condition to poll for
|
||||
* @func the function to call when the condition happens
|
||||
@ -251,7 +238,7 @@ guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond,
|
||||
* @buf the data
|
||||
* @len the number of bytes to send
|
||||
*
|
||||
* Returns: the number of bytes consumed (0 if no assicated CharDriver)
|
||||
* Returns: the number of bytes consumed (0 if no assicated Chardev)
|
||||
*/
|
||||
int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len);
|
||||
|
||||
@ -266,7 +253,7 @@ int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len);
|
||||
* @buf the data
|
||||
* @len the number of bytes to send
|
||||
*
|
||||
* Returns: the number of bytes consumed (0 if no assicated CharDriver)
|
||||
* Returns: the number of bytes consumed (0 if no assicated Chardev)
|
||||
*/
|
||||
int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len);
|
||||
|
||||
@ -278,7 +265,7 @@ int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len);
|
||||
* @buf the data buffer
|
||||
* @len the number of bytes to read
|
||||
*
|
||||
* Returns: the number of bytes read (0 if no assicated CharDriver)
|
||||
* Returns: the number of bytes read (0 if no assicated Chardev)
|
||||
*/
|
||||
int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len);
|
||||
|
||||
@ -291,7 +278,7 @@ int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len);
|
||||
* @arg the data associated with @cmd
|
||||
*
|
||||
* Returns: if @cmd is not supported by the backend or there is no
|
||||
* associated CharDriver, -ENOTSUP, otherwise the return
|
||||
* associated Chardev, -ENOTSUP, otherwise the return
|
||||
* value depends on the semantics of @cmd
|
||||
*/
|
||||
int qemu_chr_fe_ioctl(CharBackend *be, int cmd, void *arg);
|
||||
@ -331,7 +318,7 @@ int qemu_chr_fe_get_msgfds(CharBackend *be, int *fds, int num);
|
||||
* result in overwriting the fd array with the new value without being send.
|
||||
* Upon writing the message the fd array is freed.
|
||||
*
|
||||
* Returns: -1 if fd passing isn't supported or no associated CharDriver.
|
||||
* Returns: -1 if fd passing isn't supported or no associated Chardev.
|
||||
*/
|
||||
int qemu_chr_fe_set_msgfds(CharBackend *be, int *fds, int num);
|
||||
|
||||
@ -382,7 +369,7 @@ void qemu_chr_be_event(Chardev *s, int event);
|
||||
* @qemu_chr_fe_init:
|
||||
*
|
||||
* Initializes a front end for the given CharBackend and
|
||||
* CharDriver. Call qemu_chr_fe_deinit() to remove the association and
|
||||
* Chardev. Call qemu_chr_fe_deinit() to remove the association and
|
||||
* release the driver.
|
||||
*
|
||||
* Returns: false on error.
|
||||
@ -393,16 +380,16 @@ bool qemu_chr_fe_init(CharBackend *b, Chardev *s, Error **errp);
|
||||
* @qemu_chr_fe_get_driver:
|
||||
*
|
||||
* Returns the driver associated with a CharBackend or NULL if no
|
||||
* associated CharDriver.
|
||||
* associated Chardev.
|
||||
*/
|
||||
Chardev *qemu_chr_fe_get_driver(CharBackend *be);
|
||||
|
||||
/**
|
||||
* @qemu_chr_fe_deinit:
|
||||
*
|
||||
* Dissociate the CharBackend from the CharDriver.
|
||||
* Dissociate the CharBackend from the Chardev.
|
||||
*
|
||||
* Safe to call without associated CharDriver.
|
||||
* Safe to call without associated Chardev.
|
||||
*/
|
||||
void qemu_chr_fe_deinit(CharBackend *b);
|
||||
|
||||
@ -421,7 +408,7 @@ void qemu_chr_fe_deinit(CharBackend *b);
|
||||
* Set the front end char handlers. The front end takes the focus if
|
||||
* any of the handler is non-NULL.
|
||||
*
|
||||
* Without associated CharDriver, nothing is changed.
|
||||
* Without associated Chardev, nothing is changed.
|
||||
*/
|
||||
void qemu_chr_fe_set_handlers(CharBackend *b,
|
||||
IOCanReadHandler *fd_can_read,
|
||||
@ -436,7 +423,7 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
|
||||
*
|
||||
* Take the focus (if the front end is muxed).
|
||||
*
|
||||
* Without associated CharDriver, nothing is changed.
|
||||
* Without associated Chardev, nothing is changed.
|
||||
*/
|
||||
void qemu_chr_fe_take_focus(CharBackend *b);
|
||||
|
||||
@ -446,10 +433,12 @@ int qemu_chr_add_client(Chardev *s, int fd);
|
||||
Chardev *qemu_chr_find(const char *name);
|
||||
|
||||
bool qemu_chr_has_feature(Chardev *chr,
|
||||
CharDriverFeature feature);
|
||||
ChardevFeature feature);
|
||||
void qemu_chr_set_feature(Chardev *chr,
|
||||
CharDriverFeature feature);
|
||||
ChardevFeature feature);
|
||||
QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename);
|
||||
int qemu_chr_write_all(Chardev *s, const uint8_t *buf, int len);
|
||||
int qemu_chr_wait_connected(Chardev *chr, Error **errp);
|
||||
|
||||
#define TYPE_CHARDEV "chardev"
|
||||
#define CHARDEV(obj) OBJECT_CHECK(Chardev, (obj), TYPE_CHARDEV)
|
||||
@ -472,8 +461,6 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename);
|
||||
#define TYPE_CHARDEV_SOCKET "chardev-socket"
|
||||
#define TYPE_CHARDEV_UDP "chardev-udp"
|
||||
|
||||
#define CHARDEV_IS_MUX(chr) \
|
||||
object_dynamic_cast(OBJECT(chr), TYPE_CHARDEV_MUX)
|
||||
#define CHARDEV_IS_RINGBUF(chr) \
|
||||
object_dynamic_cast(OBJECT(chr), TYPE_CHARDEV_RINGBUF)
|
||||
#define CHARDEV_IS_PTY(chr) \
|
||||
@ -483,6 +470,7 @@ typedef struct ChardevClass {
|
||||
ObjectClass parent_class;
|
||||
|
||||
bool internal; /* TODO: eventually use TYPE_USER_CREATABLE */
|
||||
void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp);
|
||||
|
||||
void (*open)(Chardev *chr, ChardevBackend *backend,
|
||||
bool *be_opened, Error **errp);
|
||||
@ -496,24 +484,15 @@ typedef struct ChardevClass {
|
||||
int (*set_msgfds)(Chardev *s, int *fds, int num);
|
||||
int (*chr_add_client)(Chardev *chr, int fd);
|
||||
int (*chr_wait_connected)(Chardev *chr, Error **errp);
|
||||
void (*chr_free)(Chardev *chr);
|
||||
void (*chr_disconnect)(Chardev *chr);
|
||||
void (*chr_accept_input)(Chardev *chr);
|
||||
void (*chr_set_echo)(Chardev *chr, bool echo);
|
||||
void (*chr_set_fe_open)(Chardev *chr, int fe_open);
|
||||
} ChardevClass;
|
||||
|
||||
struct CharDriver {
|
||||
ChardevBackendKind kind;
|
||||
const char *alias;
|
||||
void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp);
|
||||
};
|
||||
|
||||
Chardev *qemu_chardev_new(const char *id, const char *typename,
|
||||
ChardevBackend *backend, Error **errp);
|
||||
|
||||
void register_char_driver(const CharDriver *driver);
|
||||
|
||||
extern int term_escape_char;
|
||||
|
||||
/* console.c */
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/numa.h"
|
||||
#include "monitor/monitor.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "qemu/readline.h"
|
||||
#include "ui/console.h"
|
||||
#include "ui/input.h"
|
||||
|
5171
qemu-char.c
5171
qemu-char.c
File diff suppressed because it is too large
Load Diff
1
qmp.c
1
qmp.c
@ -18,6 +18,7 @@
|
||||
#include "qemu/cutils.h"
|
||||
#include "monitor/monitor.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "qemu/uuid.h"
|
||||
#include "qmp-commands.h"
|
||||
#include "sysemu/char.h"
|
||||
|
@ -210,9 +210,9 @@ static int spice_chr_write(Chardev *chr, const uint8_t *buf, int len)
|
||||
return read_bytes;
|
||||
}
|
||||
|
||||
static void spice_chr_free(struct Chardev *chr)
|
||||
static void char_spice_finalize(Object *obj)
|
||||
{
|
||||
SpiceChardev *s = SPICE_CHARDEV(chr);
|
||||
SpiceChardev *s = SPICE_CHARDEV(obj);
|
||||
|
||||
vmc_unregister_interface(s);
|
||||
QLIST_REMOVE(s, next);
|
||||
@ -338,6 +338,7 @@ static void qemu_chr_parse_spice_vmc(QemuOpts *opts, ChardevBackend *backend,
|
||||
error_setg(errp, "chardev: spice channel: no name given");
|
||||
return;
|
||||
}
|
||||
backend->type = CHARDEV_BACKEND_KIND_SPICEVMC;
|
||||
spicevmc = backend->u.spicevmc.data = g_new0(ChardevSpiceChannel, 1);
|
||||
qemu_chr_parse_common(opts, qapi_ChardevSpiceChannel_base(spicevmc));
|
||||
spicevmc->type = g_strdup(name);
|
||||
@ -353,6 +354,7 @@ static void qemu_chr_parse_spice_port(QemuOpts *opts, ChardevBackend *backend,
|
||||
error_setg(errp, "chardev: spice port: no name given");
|
||||
return;
|
||||
}
|
||||
backend->type = CHARDEV_BACKEND_KIND_SPICEPORT;
|
||||
spiceport = backend->u.spiceport.data = g_new0(ChardevSpicePort, 1);
|
||||
qemu_chr_parse_common(opts, qapi_ChardevSpicePort_base(spiceport));
|
||||
spiceport->fqdn = g_strdup(name);
|
||||
@ -365,13 +367,13 @@ static void char_spice_class_init(ObjectClass *oc, void *data)
|
||||
cc->chr_write = spice_chr_write;
|
||||
cc->chr_add_watch = spice_chr_add_watch;
|
||||
cc->chr_accept_input = spice_chr_accept_input;
|
||||
cc->chr_free = spice_chr_free;
|
||||
}
|
||||
|
||||
static const TypeInfo char_spice_type_info = {
|
||||
.name = TYPE_CHARDEV_SPICE,
|
||||
.parent = TYPE_CHARDEV,
|
||||
.instance_size = sizeof(SpiceChardev),
|
||||
.instance_finalize = char_spice_finalize,
|
||||
.class_init = char_spice_class_init,
|
||||
.abstract = true,
|
||||
};
|
||||
@ -380,6 +382,7 @@ static void char_spicevmc_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->parse = qemu_chr_parse_spice_vmc;
|
||||
cc->open = qemu_chr_open_spice_vmc;
|
||||
cc->chr_set_fe_open = spice_vmc_set_fe_open;
|
||||
}
|
||||
@ -394,6 +397,7 @@ static void char_spiceport_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->parse = qemu_chr_parse_spice_port;
|
||||
cc->open = qemu_chr_open_spice_port;
|
||||
cc->chr_set_fe_open = spice_port_set_fe_open;
|
||||
}
|
||||
@ -406,17 +410,6 @@ static const TypeInfo char_spiceport_type_info = {
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
static const CharDriver vmc_driver = {
|
||||
.kind = CHARDEV_BACKEND_KIND_SPICEVMC,
|
||||
.parse = qemu_chr_parse_spice_vmc,
|
||||
};
|
||||
static const CharDriver port_driver = {
|
||||
.kind = CHARDEV_BACKEND_KIND_SPICEPORT,
|
||||
.parse = qemu_chr_parse_spice_port,
|
||||
};
|
||||
register_char_driver(&vmc_driver);
|
||||
register_char_driver(&port_driver);
|
||||
|
||||
type_register_static(&char_spice_type_info);
|
||||
type_register_static(&char_spicevmc_type_info);
|
||||
type_register_static(&char_spiceport_type_info);
|
||||
|
@ -9,7 +9,7 @@ SYSEMU_TARGET_LIST := $(subst -softmmu.mak,,$(notdir \
|
||||
check-unit-y = tests/check-qdict$(EXESUF)
|
||||
gcov-files-check-qdict-y = qobject/qdict.c
|
||||
check-unit-y += tests/test-char$(EXESUF)
|
||||
gcov-files-check-qdict-y = qemu-char.c
|
||||
gcov-files-check-qdict-y = chardev/char.c
|
||||
check-unit-y += tests/check-qfloat$(EXESUF)
|
||||
gcov-files-check-qfloat-y = qobject/qfloat.c
|
||||
check-unit-y += tests/check-qint$(EXESUF)
|
||||
@ -510,7 +510,8 @@ tests/check-qjson$(EXESUF): tests/check-qjson.o $(test-util-obj-y)
|
||||
tests/check-qom-interface$(EXESUF): tests/check-qom-interface.o $(test-qom-obj-y)
|
||||
tests/check-qom-proplist$(EXESUF): tests/check-qom-proplist.o $(test-qom-obj-y)
|
||||
|
||||
tests/test-char$(EXESUF): tests/test-char.o qemu-char.o qemu-timer.o $(test-util-obj-y) $(qtest-obj-y) $(test-block-obj-y)
|
||||
tests/test-char$(EXESUF): tests/test-char.o qemu-timer.o \
|
||||
$(test-util-obj-y) $(qtest-obj-y) $(test-block-obj-y) $(chardev-obj-y)
|
||||
tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(test-block-obj-y)
|
||||
tests/test-aio$(EXESUF): tests/test-aio.o $(test-block-obj-y)
|
||||
tests/test-throttle$(EXESUF): tests/test-throttle.o $(test-block-obj-y)
|
||||
@ -703,7 +704,9 @@ tests/usb-hcd-ehci-test$(EXESUF): tests/usb-hcd-ehci-test.o $(libqos-usb-obj-y)
|
||||
tests/usb-hcd-xhci-test$(EXESUF): tests/usb-hcd-xhci-test.o $(libqos-usb-obj-y)
|
||||
tests/pc-cpu-test$(EXESUF): tests/pc-cpu-test.o
|
||||
tests/postcopy-test$(EXESUF): tests/postcopy-test.o
|
||||
tests/vhost-user-test$(EXESUF): tests/vhost-user-test.o qemu-char.o qemu-timer.o $(qtest-obj-y) $(test-io-obj-y) $(libqos-virtio-obj-y) $(libqos-pc-obj-y)
|
||||
tests/vhost-user-test$(EXESUF): tests/vhost-user-test.o qemu-timer.o \
|
||||
$(qtest-obj-y) $(test-io-obj-y) $(libqos-virtio-obj-y) $(libqos-pc-obj-y) \
|
||||
$(chardev-obj-y)
|
||||
tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o
|
||||
tests/test-qemu-opts$(EXESUF): tests/test-qemu-opts.o $(test-util-obj-y)
|
||||
tests/test-write-threshold$(EXESUF): tests/test-write-threshold.o $(test-block-obj-y)
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include "libqtest.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/range.h"
|
||||
#include "qemu/sockets.h"
|
||||
|
10
ui/console.c
10
ui/console.c
@ -2059,8 +2059,6 @@ static void text_console_do_init(Chardev *chr, DisplayState *ds)
|
||||
qemu_chr_be_generic_open(chr);
|
||||
}
|
||||
|
||||
static const CharDriver vc_driver;
|
||||
|
||||
static void vc_chr_open(Chardev *chr,
|
||||
ChardevBackend *backend,
|
||||
bool *be_opened,
|
||||
@ -2150,6 +2148,7 @@ void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, Error **errp)
|
||||
int val;
|
||||
ChardevVC *vc;
|
||||
|
||||
backend->type = CHARDEV_BACKEND_KIND_VC;
|
||||
vc = backend->u.vc.data = g_new0(ChardevVC, 1);
|
||||
qemu_chr_parse_common(opts, qapi_ChardevVC_base(vc));
|
||||
|
||||
@ -2189,6 +2188,7 @@ static void char_vc_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->parse = qemu_chr_parse_vc;
|
||||
cc->open = vc_chr_open;
|
||||
cc->chr_write = vc_chr_write;
|
||||
cc->chr_set_echo = vc_chr_set_echo;
|
||||
@ -2206,15 +2206,9 @@ void qemu_console_early_init(void)
|
||||
/* set the default vc driver */
|
||||
if (!object_class_by_name(TYPE_CHARDEV_VC)) {
|
||||
type_register(&char_vc_type_info);
|
||||
register_char_driver(&vc_driver);
|
||||
}
|
||||
}
|
||||
|
||||
static const CharDriver vc_driver = {
|
||||
.kind = CHARDEV_BACKEND_KIND_VC,
|
||||
.parse = qemu_chr_parse_vc,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&qemu_console_info);
|
||||
|
9
ui/gtk.c
9
ui/gtk.c
@ -1739,8 +1739,6 @@ static void gd_vc_chr_set_echo(Chardev *chr, bool echo)
|
||||
|
||||
static int nb_vcs;
|
||||
static Chardev *vcs[MAX_VCS];
|
||||
static const CharDriver gd_vc_driver;
|
||||
|
||||
static void gd_vc_open(Chardev *chr,
|
||||
ChardevBackend *backend,
|
||||
bool *be_opened,
|
||||
@ -1763,6 +1761,7 @@ static void char_gd_vc_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->parse = qemu_chr_parse_vc;
|
||||
cc->open = gd_vc_open;
|
||||
cc->chr_write = gd_vc_chr_write;
|
||||
cc->chr_set_echo = gd_vc_chr_set_echo;
|
||||
@ -1775,11 +1774,6 @@ static const TypeInfo char_gd_vc_type_info = {
|
||||
.class_init = char_gd_vc_class_init,
|
||||
};
|
||||
|
||||
static const CharDriver gd_vc_driver = {
|
||||
.kind = CHARDEV_BACKEND_KIND_VC,
|
||||
.parse = qemu_chr_parse_vc,
|
||||
};
|
||||
|
||||
static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size,
|
||||
gpointer user_data)
|
||||
{
|
||||
@ -2383,6 +2377,5 @@ void early_gtk_display_init(int opengl)
|
||||
|
||||
#if defined(CONFIG_VTE)
|
||||
type_register(&char_gd_vc_type_info);
|
||||
register_char_driver(&gd_vc_driver);
|
||||
#endif
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user