4 * Copyright (c) 2003-2008 Fabrice Bellard
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 #include "qemu/osdep.h"
26 #include "qapi/error.h"
27 #include "qemu/option.h"
28 #include "chardev/char.h"
29 #include "sysemu/block-backend.h"
30 #include "chardev/char-mux.h"
32 /* MUX driver for serial I/O splitting */
34 /* Called with chr_write_lock held. */
35 static int mux_chr_write(Chardev
*chr
, const uint8_t *buf
, int len
)
37 MuxChardev
*d
= MUX_CHARDEV(chr
);
40 ret
= qemu_chr_fe_write(&d
->chr
, buf
, len
);
45 for (i
= 0; i
< len
; i
++) {
51 ti
= qemu_clock_get_ms(QEMU_CLOCK_REALTIME
);
52 if (d
->timestamps_start
== -1) {
53 d
->timestamps_start
= ti
;
55 ti
-= d
->timestamps_start
;
57 snprintf(buf1
, sizeof(buf1
),
58 "[%02d:%02d:%02d.%03d] ",
63 /* XXX this blocks entire thread. Rewrite to use
64 * qemu_chr_fe_write and background I/O callbacks */
65 qemu_chr_fe_write_all(&d
->chr
,
66 (uint8_t *)buf1
, strlen(buf1
));
69 ret
+= qemu_chr_fe_write(&d
->chr
, buf
+ i
, 1);
78 static const char * const mux_help
[] = {
79 "% h print this help\n\r",
80 "% x exit emulator\n\r",
81 "% s save disk data back to file (if -snapshot)\n\r",
82 "% t toggle console timestamps\n\r",
83 "% b send break (magic sysrq)\n\r",
84 "% c switch between console and monitor\n\r",
89 int term_escape_char
= 0x01; /* ctrl-a is used for escape */
90 static void mux_print_help(Chardev
*chr
)
93 char ebuf
[15] = "Escape-Char";
94 char cbuf
[50] = "\n\r";
96 if (term_escape_char
> 0 && term_escape_char
< 26) {
97 snprintf(cbuf
, sizeof(cbuf
), "\n\r");
98 snprintf(ebuf
, sizeof(ebuf
), "C-%c", term_escape_char
- 1 + 'a');
100 snprintf(cbuf
, sizeof(cbuf
),
101 "\n\rEscape-Char set to Ascii: 0x%02x\n\r\n\r",
104 /* XXX this blocks entire thread. Rewrite to use
105 * qemu_chr_fe_write and background I/O callbacks */
106 qemu_chr_write_all(chr
, (uint8_t *)cbuf
, strlen(cbuf
));
107 for (i
= 0; mux_help
[i
] != NULL
; i
++) {
108 for (j
= 0; mux_help
[i
][j
] != '\0'; j
++) {
109 if (mux_help
[i
][j
] == '%') {
110 qemu_chr_write_all(chr
, (uint8_t *)ebuf
, strlen(ebuf
));
112 qemu_chr_write_all(chr
, (uint8_t *)&mux_help
[i
][j
], 1);
118 static void mux_chr_send_event(MuxChardev
*d
, int mux_nr
, int event
)
120 CharBackend
*be
= d
->backends
[mux_nr
];
122 if (be
&& be
->chr_event
) {
123 be
->chr_event(be
->opaque
, event
);
127 static void mux_chr_be_event(Chardev
*chr
, int event
)
129 MuxChardev
*d
= MUX_CHARDEV(chr
);
131 if (d
->focus
!= -1) {
132 mux_chr_send_event(d
, d
->focus
, event
);
136 static int mux_proc_byte(Chardev
*chr
, MuxChardev
*d
, int ch
)
138 if (d
->term_got_escape
) {
139 d
->term_got_escape
= 0;
140 if (ch
== term_escape_char
) {
150 const char *term
= "QEMU: Terminated\n\r";
151 qemu_chr_write_all(chr
, (uint8_t *)term
, strlen(term
));
159 qemu_chr_be_event(chr
, CHR_EVENT_BREAK
);
162 assert(d
->mux_cnt
> 0); /* handler registered with first fe */
163 /* Switch to the next registered device */
164 mux_set_focus(chr
, (d
->focus
+ 1) % d
->mux_cnt
);
167 d
->timestamps
= !d
->timestamps
;
168 d
->timestamps_start
= -1;
172 } else if (ch
== term_escape_char
) {
173 d
->term_got_escape
= 1;
181 static void mux_chr_accept_input(Chardev
*chr
)
183 MuxChardev
*d
= MUX_CHARDEV(chr
);
185 CharBackend
*be
= d
->backends
[m
];
187 while (be
&& d
->prod
[m
] != d
->cons
[m
] &&
188 be
->chr_can_read
&& be
->chr_can_read(be
->opaque
)) {
189 be
->chr_read(be
->opaque
,
190 &d
->buffer
[m
][d
->cons
[m
]++ & MUX_BUFFER_MASK
], 1);
194 static int mux_chr_can_read(void *opaque
)
196 MuxChardev
*d
= MUX_CHARDEV(opaque
);
198 CharBackend
*be
= d
->backends
[m
];
200 if ((d
->prod
[m
] - d
->cons
[m
]) < MUX_BUFFER_SIZE
) {
204 if (be
&& be
->chr_can_read
) {
205 return be
->chr_can_read(be
->opaque
);
211 static void mux_chr_read(void *opaque
, const uint8_t *buf
, int size
)
213 Chardev
*chr
= CHARDEV(opaque
);
214 MuxChardev
*d
= MUX_CHARDEV(opaque
);
216 CharBackend
*be
= d
->backends
[m
];
219 mux_chr_accept_input(opaque
);
221 for (i
= 0; i
< size
; i
++)
222 if (mux_proc_byte(chr
, d
, buf
[i
])) {
223 if (d
->prod
[m
] == d
->cons
[m
] &&
224 be
&& be
->chr_can_read
&&
225 be
->chr_can_read(be
->opaque
)) {
226 be
->chr_read(be
->opaque
, &buf
[i
], 1);
228 d
->buffer
[m
][d
->prod
[m
]++ & MUX_BUFFER_MASK
] = buf
[i
];
235 void mux_chr_send_all_event(Chardev
*chr
, int event
)
237 MuxChardev
*d
= MUX_CHARDEV(chr
);
240 if (!muxes_realized
) {
244 /* Send the event to all registered listeners */
245 for (i
= 0; i
< d
->mux_cnt
; i
++) {
246 mux_chr_send_event(d
, i
, event
);
250 static void mux_chr_event(void *opaque
, int event
)
252 mux_chr_send_all_event(CHARDEV(opaque
), event
);
255 static GSource
*mux_chr_add_watch(Chardev
*s
, GIOCondition cond
)
257 MuxChardev
*d
= MUX_CHARDEV(s
);
258 Chardev
*chr
= qemu_chr_fe_get_driver(&d
->chr
);
259 ChardevClass
*cc
= CHARDEV_GET_CLASS(chr
);
261 if (!cc
->chr_add_watch
) {
265 return cc
->chr_add_watch(chr
, cond
);
268 static void char_mux_finalize(Object
*obj
)
270 MuxChardev
*d
= MUX_CHARDEV(obj
);
273 for (i
= 0; i
< d
->mux_cnt
; i
++) {
274 CharBackend
*be
= d
->backends
[i
];
279 qemu_chr_fe_deinit(&d
->chr
, false);
282 void mux_chr_set_handlers(Chardev
*chr
, GMainContext
*context
)
284 MuxChardev
*d
= MUX_CHARDEV(chr
);
286 /* Fix up the real driver with mux routines */
287 qemu_chr_fe_set_handlers(&d
->chr
,
296 void mux_set_focus(Chardev
*chr
, int focus
)
298 MuxChardev
*d
= MUX_CHARDEV(chr
);
301 assert(focus
< d
->mux_cnt
);
303 if (d
->focus
!= -1) {
304 mux_chr_send_event(d
, d
->focus
, CHR_EVENT_MUX_OUT
);
308 mux_chr_send_event(d
, d
->focus
, CHR_EVENT_MUX_IN
);
311 static void qemu_chr_open_mux(Chardev
*chr
,
312 ChardevBackend
*backend
,
316 ChardevMux
*mux
= backend
->u
.mux
.data
;
318 MuxChardev
*d
= MUX_CHARDEV(chr
);
320 drv
= qemu_chr_find(mux
->chardev
);
322 error_setg(errp
, "mux: base chardev %s not found", mux
->chardev
);
327 /* only default to opened state if we've realized the initial
330 *be_opened
= muxes_realized
;
331 qemu_chr_fe_init(&d
->chr
, drv
, errp
);
334 static void qemu_chr_parse_mux(QemuOpts
*opts
, ChardevBackend
*backend
,
337 const char *chardev
= qemu_opt_get(opts
, "chardev");
340 if (chardev
== NULL
) {
341 error_setg(errp
, "chardev: mux: no chardev given");
344 backend
->type
= CHARDEV_BACKEND_KIND_MUX
;
345 mux
= backend
->u
.mux
.data
= g_new0(ChardevMux
, 1);
346 qemu_chr_parse_common(opts
, qapi_ChardevMux_base(mux
));
347 mux
->chardev
= g_strdup(chardev
);
350 static void char_mux_class_init(ObjectClass
*oc
, void *data
)
352 ChardevClass
*cc
= CHARDEV_CLASS(oc
);
354 cc
->parse
= qemu_chr_parse_mux
;
355 cc
->open
= qemu_chr_open_mux
;
356 cc
->chr_write
= mux_chr_write
;
357 cc
->chr_accept_input
= mux_chr_accept_input
;
358 cc
->chr_add_watch
= mux_chr_add_watch
;
359 cc
->chr_be_event
= mux_chr_be_event
;
362 static const TypeInfo char_mux_type_info
= {
363 .name
= TYPE_CHARDEV_MUX
,
364 .parent
= TYPE_CHARDEV
,
365 .class_init
= char_mux_class_init
,
366 .instance_size
= sizeof(MuxChardev
),
367 .instance_finalize
= char_mux_finalize
,
370 static void register_types(void)
372 type_register_static(&char_mux_type_info
);
375 type_init(register_types
);