mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
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:
+2
-1
@@ -1194,8 +1194,9 @@ T: git git://github.com/jnsnow/qemu.git bitmaps
|
|||||||
|
|
||||||
Character device backends
|
Character device backends
|
||||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
M: Marc-André Lureau <marcandre.lureau@redhat.com>
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: qemu-char.c
|
F: chardev/
|
||||||
F: backends/msmouse.c
|
F: backends/msmouse.c
|
||||||
F: backends/testdev.c
|
F: backends/testdev.c
|
||||||
|
|
||||||
|
|||||||
@@ -147,6 +147,7 @@ endif
|
|||||||
|
|
||||||
dummy := $(call unnest-vars,, \
|
dummy := $(call unnest-vars,, \
|
||||||
stub-obj-y \
|
stub-obj-y \
|
||||||
|
chardev-obj-y \
|
||||||
util-obj-y \
|
util-obj-y \
|
||||||
qga-obj-y \
|
qga-obj-y \
|
||||||
ivshmem-client-obj-y \
|
ivshmem-client-obj-y \
|
||||||
@@ -223,7 +224,8 @@ subdir-dtc:dtc/libfdt dtc/tests
|
|||||||
dtc/%:
|
dtc/%:
|
||||||
mkdir -p $@
|
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))
|
ROMSUBDIR_RULES=$(patsubst %,romsubdir-%, $(ROMS))
|
||||||
# Only keep -O and -g cflags
|
# Only keep -O and -g cflags
|
||||||
|
|||||||
+3
-2
@@ -4,6 +4,8 @@ stub-obj-y = stubs/ crypto/
|
|||||||
util-obj-y = util/ qobject/ qapi/
|
util-obj-y = util/ qobject/ qapi/
|
||||||
util-obj-y += qmp-introspect.o qapi-types.o qapi-visit.o qapi-event.o
|
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
|
# 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-$(CONFIG_LINUX) += fsdev/
|
||||||
|
|
||||||
common-obj-y += migration/
|
common-obj-y += migration/
|
||||||
common-obj-y += qemu-char.o #aio.o
|
common-obj-y += page_cache.o #aio.o
|
||||||
common-obj-y += page_cache.o
|
|
||||||
|
|
||||||
common-obj-$(CONFIG_SPICE) += spice-qemu-char.o
|
common-obj-$(CONFIG_SPICE) += spice-qemu-char.o
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -172,12 +172,14 @@ all-obj-y := $(obj-y)
|
|||||||
target-obj-y :=
|
target-obj-y :=
|
||||||
block-obj-y :=
|
block-obj-y :=
|
||||||
common-obj-y :=
|
common-obj-y :=
|
||||||
|
chardev-obj-y :=
|
||||||
include $(SRC_PATH)/Makefile.objs
|
include $(SRC_PATH)/Makefile.objs
|
||||||
dummy := $(call unnest-vars,,target-obj-y)
|
dummy := $(call unnest-vars,,target-obj-y)
|
||||||
target-obj-y-save := $(target-obj-y)
|
target-obj-y-save := $(target-obj-y)
|
||||||
dummy := $(call unnest-vars,.., \
|
dummy := $(call unnest-vars,.., \
|
||||||
block-obj-y \
|
block-obj-y \
|
||||||
block-obj-m \
|
block-obj-m \
|
||||||
|
chardev-obj-y \
|
||||||
crypto-obj-y \
|
crypto-obj-y \
|
||||||
crypto-aes-obj-y \
|
crypto-aes-obj-y \
|
||||||
qom-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 += $(common-obj-y)
|
||||||
all-obj-y += $(target-obj-y)
|
all-obj-y += $(target-obj-y)
|
||||||
all-obj-y += $(qom-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_USER_ONLY) += $(crypto-aes-obj-y)
|
||||||
all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y)
|
all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y)
|
||||||
all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y)
|
all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y)
|
||||||
|
|||||||
+3
-8
@@ -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);
|
timer_free(baum->cellCount_timer);
|
||||||
if (baum->brlapi) {
|
if (baum->brlapi) {
|
||||||
@@ -659,23 +659,18 @@ static void char_braille_class_init(ObjectClass *oc, void *data)
|
|||||||
cc->open = baum_chr_open;
|
cc->open = baum_chr_open;
|
||||||
cc->chr_write = baum_chr_write;
|
cc->chr_write = baum_chr_write;
|
||||||
cc->chr_accept_input = baum_chr_accept_input;
|
cc->chr_accept_input = baum_chr_accept_input;
|
||||||
cc->chr_free = baum_chr_free;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo char_braille_type_info = {
|
static const TypeInfo char_braille_type_info = {
|
||||||
.name = TYPE_CHARDEV_BRAILLE,
|
.name = TYPE_CHARDEV_BRAILLE,
|
||||||
.parent = TYPE_CHARDEV,
|
.parent = TYPE_CHARDEV,
|
||||||
.instance_size = sizeof(BaumChardev),
|
.instance_size = sizeof(BaumChardev),
|
||||||
|
.instance_finalize = char_braille_finalize,
|
||||||
.class_init = char_braille_class_init,
|
.class_init = char_braille_class_init,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void register_types(void)
|
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);
|
type_register_static(&char_braille_type_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-8
@@ -139,9 +139,9 @@ static int msmouse_chr_write(struct Chardev *s, const uint8_t *buf, int len)
|
|||||||
return 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);
|
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->open = msmouse_chr_open;
|
||||||
cc->chr_write = msmouse_chr_write;
|
cc->chr_write = msmouse_chr_write;
|
||||||
cc->chr_accept_input = msmouse_chr_accept_input;
|
cc->chr_accept_input = msmouse_chr_accept_input;
|
||||||
cc->chr_free = msmouse_chr_free;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo char_msmouse_type_info = {
|
static const TypeInfo char_msmouse_type_info = {
|
||||||
.name = TYPE_CHARDEV_MSMOUSE,
|
.name = TYPE_CHARDEV_MSMOUSE,
|
||||||
.parent = TYPE_CHARDEV,
|
.parent = TYPE_CHARDEV,
|
||||||
.instance_size = sizeof(MouseChardev),
|
.instance_size = sizeof(MouseChardev),
|
||||||
|
.instance_finalize = char_msmouse_finalize,
|
||||||
.class_init = char_msmouse_class_init,
|
.class_init = char_msmouse_class_init,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void register_types(void)
|
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);
|
type_register_static(&char_msmouse_type_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -123,11 +123,6 @@ static const TypeInfo char_testdev_type_info = {
|
|||||||
|
|
||||||
static void register_types(void)
|
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);
|
type_register_static(&char_testdev_type_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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);
|
||||||
@@ -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);
|
||||||
@@ -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 */
|
||||||
@@ -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);
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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 */
|
||||||
@@ -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);
|
||||||
@@ -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 */
|
||||||
@@ -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);
|
||||||
@@ -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
|
||||||
@@ -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 */
|
||||||
@@ -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);
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user