-----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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Peter Maydell
						Peter Maydell