xref: /openbmc/qemu/chardev/char-mux.c (revision 412df552860f196f0dd51f5cd621ce721705caa5)
1df85a78bSMarc-André Lureau /*
2df85a78bSMarc-André Lureau  * QEMU System Emulator
3df85a78bSMarc-André Lureau  *
4df85a78bSMarc-André Lureau  * Copyright (c) 2003-2008 Fabrice Bellard
5df85a78bSMarc-André Lureau  *
6df85a78bSMarc-André Lureau  * Permission is hereby granted, free of charge, to any person obtaining a copy
7df85a78bSMarc-André Lureau  * of this software and associated documentation files (the "Software"), to deal
8df85a78bSMarc-André Lureau  * in the Software without restriction, including without limitation the rights
9df85a78bSMarc-André Lureau  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10df85a78bSMarc-André Lureau  * copies of the Software, and to permit persons to whom the Software is
11df85a78bSMarc-André Lureau  * furnished to do so, subject to the following conditions:
12df85a78bSMarc-André Lureau  *
13df85a78bSMarc-André Lureau  * The above copyright notice and this permission notice shall be included in
14df85a78bSMarc-André Lureau  * all copies or substantial portions of the Software.
15df85a78bSMarc-André Lureau  *
16df85a78bSMarc-André Lureau  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17df85a78bSMarc-André Lureau  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18df85a78bSMarc-André Lureau  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19df85a78bSMarc-André Lureau  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20df85a78bSMarc-André Lureau  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21df85a78bSMarc-André Lureau  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22df85a78bSMarc-André Lureau  * THE SOFTWARE.
23df85a78bSMarc-André Lureau  */
24922a01a0SMarkus Armbruster 
25df85a78bSMarc-André Lureau #include "qemu/osdep.h"
26df85a78bSMarc-André Lureau #include "qapi/error.h"
270b8fa32fSMarkus Armbruster #include "qemu/module.h"
28922a01a0SMarkus Armbruster #include "qemu/option.h"
29005b6d51SRoman Penyaev #include "qemu/bitops.h"
308228e353SMarc-André Lureau #include "chardev/char.h"
31df85a78bSMarc-André Lureau #include "sysemu/block-backend.h"
32a00e37a4SAlex Bennée #include "qapi/qapi-commands-control.h"
33ffa0f7ebSPhilippe Mathieu-Daudé #include "chardev-internal.h"
34df85a78bSMarc-André Lureau 
35df85a78bSMarc-André Lureau /* MUX driver for serial I/O splitting */
36df85a78bSMarc-André Lureau 
375a1ee607SPaolo Bonzini /*
385a1ee607SPaolo Bonzini  * Set to false by suspend_mux_open.  Open events are delayed until
395a1ee607SPaolo Bonzini  * resume_mux_open.  Usually suspend_mux_open is called before
405a1ee607SPaolo Bonzini  * command line processing and resume_mux_open afterwards.
415a1ee607SPaolo Bonzini  */
425a1ee607SPaolo Bonzini static bool muxes_opened = true;
435a1ee607SPaolo Bonzini 
44df85a78bSMarc-André Lureau /* Called with chr_write_lock held.  */
mux_chr_write(Chardev * chr,const uint8_t * buf,int len)45df85a78bSMarc-André Lureau static int mux_chr_write(Chardev *chr, const uint8_t *buf, int len)
46df85a78bSMarc-André Lureau {
47df85a78bSMarc-André Lureau     MuxChardev *d = MUX_CHARDEV(chr);
48df85a78bSMarc-André Lureau     int ret;
49df85a78bSMarc-André Lureau     if (!d->timestamps) {
50df85a78bSMarc-André Lureau         ret = qemu_chr_fe_write(&d->chr, buf, len);
51df85a78bSMarc-André Lureau     } else {
52df85a78bSMarc-André Lureau         int i;
53df85a78bSMarc-André Lureau 
54df85a78bSMarc-André Lureau         ret = 0;
55df85a78bSMarc-André Lureau         for (i = 0; i < len; i++) {
56df85a78bSMarc-André Lureau             if (d->linestart) {
57df85a78bSMarc-André Lureau                 char buf1[64];
58df85a78bSMarc-André Lureau                 int64_t ti;
59df85a78bSMarc-André Lureau                 int secs;
60df85a78bSMarc-André Lureau 
61df85a78bSMarc-André Lureau                 ti = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
62df85a78bSMarc-André Lureau                 if (d->timestamps_start == -1) {
63df85a78bSMarc-André Lureau                     d->timestamps_start = ti;
64df85a78bSMarc-André Lureau                 }
65df85a78bSMarc-André Lureau                 ti -= d->timestamps_start;
66df85a78bSMarc-André Lureau                 secs = ti / 1000;
67df85a78bSMarc-André Lureau                 snprintf(buf1, sizeof(buf1),
68df85a78bSMarc-André Lureau                          "[%02d:%02d:%02d.%03d] ",
69df85a78bSMarc-André Lureau                          secs / 3600,
70df85a78bSMarc-André Lureau                          (secs / 60) % 60,
71df85a78bSMarc-André Lureau                          secs % 60,
72df85a78bSMarc-André Lureau                          (int)(ti % 1000));
73df85a78bSMarc-André Lureau                 /* XXX this blocks entire thread. Rewrite to use
74df85a78bSMarc-André Lureau                  * qemu_chr_fe_write and background I/O callbacks */
75df85a78bSMarc-André Lureau                 qemu_chr_fe_write_all(&d->chr,
76df85a78bSMarc-André Lureau                                       (uint8_t *)buf1, strlen(buf1));
771ba39940SRoman Penyaev                 d->linestart = false;
78df85a78bSMarc-André Lureau             }
79df85a78bSMarc-André Lureau             ret += qemu_chr_fe_write(&d->chr, buf + i, 1);
80df85a78bSMarc-André Lureau             if (buf[i] == '\n') {
811ba39940SRoman Penyaev                 d->linestart = true;
82df85a78bSMarc-André Lureau             }
83df85a78bSMarc-André Lureau         }
84df85a78bSMarc-André Lureau     }
85df85a78bSMarc-André Lureau     return ret;
86df85a78bSMarc-André Lureau }
87df85a78bSMarc-André Lureau 
88df85a78bSMarc-André Lureau static const char * const mux_help[] = {
89df85a78bSMarc-André Lureau     "% h    print this help\n\r",
90df85a78bSMarc-André Lureau     "% x    exit emulator\n\r",
91df85a78bSMarc-André Lureau     "% s    save disk data back to file (if -snapshot)\n\r",
92df85a78bSMarc-André Lureau     "% t    toggle console timestamps\n\r",
93df85a78bSMarc-André Lureau     "% b    send break (magic sysrq)\n\r",
94df85a78bSMarc-André Lureau     "% c    switch between console and monitor\n\r",
95df85a78bSMarc-André Lureau     "% %  sends %\n\r",
96df85a78bSMarc-André Lureau     NULL
97df85a78bSMarc-André Lureau };
98df85a78bSMarc-André Lureau 
99df85a78bSMarc-André Lureau int term_escape_char = 0x01; /* ctrl-a is used for escape */
mux_print_help(Chardev * chr)100df85a78bSMarc-André Lureau static void mux_print_help(Chardev *chr)
101df85a78bSMarc-André Lureau {
102df85a78bSMarc-André Lureau     int i, j;
103df85a78bSMarc-André Lureau     char ebuf[15] = "Escape-Char";
104df85a78bSMarc-André Lureau     char cbuf[50] = "\n\r";
105df85a78bSMarc-André Lureau 
106df85a78bSMarc-André Lureau     if (term_escape_char > 0 && term_escape_char < 26) {
107df85a78bSMarc-André Lureau         snprintf(cbuf, sizeof(cbuf), "\n\r");
108df85a78bSMarc-André Lureau         snprintf(ebuf, sizeof(ebuf), "C-%c", term_escape_char - 1 + 'a');
109df85a78bSMarc-André Lureau     } else {
110df85a78bSMarc-André Lureau         snprintf(cbuf, sizeof(cbuf),
111df85a78bSMarc-André Lureau                  "\n\rEscape-Char set to Ascii: 0x%02x\n\r\n\r",
112df85a78bSMarc-André Lureau                  term_escape_char);
113df85a78bSMarc-André Lureau     }
114df85a78bSMarc-André Lureau     /* XXX this blocks entire thread. Rewrite to use
115df85a78bSMarc-André Lureau      * qemu_chr_fe_write and background I/O callbacks */
116df85a78bSMarc-André Lureau     qemu_chr_write_all(chr, (uint8_t *)cbuf, strlen(cbuf));
117df85a78bSMarc-André Lureau     for (i = 0; mux_help[i] != NULL; i++) {
118df85a78bSMarc-André Lureau         for (j = 0; mux_help[i][j] != '\0'; j++) {
119df85a78bSMarc-André Lureau             if (mux_help[i][j] == '%') {
120df85a78bSMarc-André Lureau                 qemu_chr_write_all(chr, (uint8_t *)ebuf, strlen(ebuf));
121df85a78bSMarc-André Lureau             } else {
122df85a78bSMarc-André Lureau                 qemu_chr_write_all(chr, (uint8_t *)&mux_help[i][j], 1);
123df85a78bSMarc-André Lureau             }
124df85a78bSMarc-André Lureau         }
125df85a78bSMarc-André Lureau     }
126df85a78bSMarc-André Lureau }
127df85a78bSMarc-André Lureau 
mux_chr_send_event(MuxChardev * d,unsigned int mux_nr,QEMUChrEvent event)128c64f0bc1SRoman Penyaev static void mux_chr_send_event(MuxChardev *d, unsigned int mux_nr,
129c64f0bc1SRoman Penyaev                                QEMUChrEvent event)
130df85a78bSMarc-André Lureau {
131df85a78bSMarc-André Lureau     CharBackend *be = d->backends[mux_nr];
132df85a78bSMarc-André Lureau 
133df85a78bSMarc-André Lureau     if (be && be->chr_event) {
134df85a78bSMarc-André Lureau         be->chr_event(be->opaque, event);
135df85a78bSMarc-André Lureau     }
136df85a78bSMarc-André Lureau }
137df85a78bSMarc-André Lureau 
mux_chr_be_event(Chardev * chr,QEMUChrEvent event)1382fa9044aSPaolo Bonzini static void mux_chr_be_event(Chardev *chr, QEMUChrEvent event)
139d09c4a47SMarc-André Lureau {
140d09c4a47SMarc-André Lureau     MuxChardev *d = MUX_CHARDEV(chr);
141d09c4a47SMarc-André Lureau 
142d09c4a47SMarc-André Lureau     if (d->focus != -1) {
143d09c4a47SMarc-André Lureau         mux_chr_send_event(d, d->focus, event);
144d09c4a47SMarc-André Lureau     }
145d09c4a47SMarc-André Lureau }
146d09c4a47SMarc-André Lureau 
mux_proc_byte(Chardev * chr,MuxChardev * d,int ch)147df85a78bSMarc-André Lureau static int mux_proc_byte(Chardev *chr, MuxChardev *d, int ch)
148df85a78bSMarc-André Lureau {
149df85a78bSMarc-André Lureau     if (d->term_got_escape) {
1501ba39940SRoman Penyaev         d->term_got_escape = false;
151df85a78bSMarc-André Lureau         if (ch == term_escape_char) {
152df85a78bSMarc-André Lureau             goto send_char;
153df85a78bSMarc-André Lureau         }
154df85a78bSMarc-André Lureau         switch (ch) {
155df85a78bSMarc-André Lureau         case '?':
156df85a78bSMarc-André Lureau         case 'h':
157df85a78bSMarc-André Lureau             mux_print_help(chr);
158df85a78bSMarc-André Lureau             break;
159df85a78bSMarc-André Lureau         case 'x':
160df85a78bSMarc-André Lureau             {
161df85a78bSMarc-André Lureau                  const char *term =  "QEMU: Terminated\n\r";
162df85a78bSMarc-André Lureau                  qemu_chr_write_all(chr, (uint8_t *)term, strlen(term));
163a00e37a4SAlex Bennée                  qmp_quit(NULL);
164df85a78bSMarc-André Lureau                  break;
165df85a78bSMarc-André Lureau             }
166df85a78bSMarc-André Lureau         case 's':
167df85a78bSMarc-André Lureau             blk_commit_all();
168df85a78bSMarc-André Lureau             break;
169df85a78bSMarc-André Lureau         case 'b':
170df85a78bSMarc-André Lureau             qemu_chr_be_event(chr, CHR_EVENT_BREAK);
171df85a78bSMarc-André Lureau             break;
172005b6d51SRoman Penyaev         case 'c': {
173005b6d51SRoman Penyaev             unsigned int bit;
174005b6d51SRoman Penyaev 
175005b6d51SRoman Penyaev             /* Handler registered with first fe */
176005b6d51SRoman Penyaev             assert(d->mux_bitset != 0);
177df85a78bSMarc-André Lureau             /* Switch to the next registered device */
178005b6d51SRoman Penyaev             bit = find_next_bit(&d->mux_bitset, MAX_MUX, d->focus + 1);
179005b6d51SRoman Penyaev             if (bit >= MAX_MUX) {
180005b6d51SRoman Penyaev                 bit = find_next_bit(&d->mux_bitset, MAX_MUX, 0);
181005b6d51SRoman Penyaev             }
182005b6d51SRoman Penyaev             mux_set_focus(chr, bit);
183df85a78bSMarc-André Lureau             break;
184005b6d51SRoman Penyaev         } case 't':
185df85a78bSMarc-André Lureau             d->timestamps = !d->timestamps;
186df85a78bSMarc-André Lureau             d->timestamps_start = -1;
1871ba39940SRoman Penyaev             d->linestart = false;
188df85a78bSMarc-André Lureau             break;
189df85a78bSMarc-André Lureau         }
190df85a78bSMarc-André Lureau     } else if (ch == term_escape_char) {
1911ba39940SRoman Penyaev         d->term_got_escape = true;
192df85a78bSMarc-André Lureau     } else {
193df85a78bSMarc-André Lureau     send_char:
194df85a78bSMarc-André Lureau         return 1;
195df85a78bSMarc-André Lureau     }
196df85a78bSMarc-André Lureau     return 0;
197df85a78bSMarc-André Lureau }
198df85a78bSMarc-André Lureau 
mux_chr_accept_input(Chardev * chr)199df85a78bSMarc-André Lureau static void mux_chr_accept_input(Chardev *chr)
200df85a78bSMarc-André Lureau {
201df85a78bSMarc-André Lureau     MuxChardev *d = MUX_CHARDEV(chr);
202df85a78bSMarc-André Lureau     int m = d->focus;
203df85a78bSMarc-André Lureau     CharBackend *be = d->backends[m];
204df85a78bSMarc-André Lureau 
205df85a78bSMarc-André Lureau     while (be && d->prod[m] != d->cons[m] &&
206df85a78bSMarc-André Lureau            be->chr_can_read && be->chr_can_read(be->opaque)) {
207df85a78bSMarc-André Lureau         be->chr_read(be->opaque,
208df85a78bSMarc-André Lureau                      &d->buffer[m][d->cons[m]++ & MUX_BUFFER_MASK], 1);
209df85a78bSMarc-André Lureau     }
210df85a78bSMarc-André Lureau }
211df85a78bSMarc-André Lureau 
mux_chr_can_read(void * opaque)212df85a78bSMarc-André Lureau static int mux_chr_can_read(void *opaque)
213df85a78bSMarc-André Lureau {
214df85a78bSMarc-André Lureau     MuxChardev *d = MUX_CHARDEV(opaque);
215df85a78bSMarc-André Lureau     int m = d->focus;
216df85a78bSMarc-André Lureau     CharBackend *be = d->backends[m];
217df85a78bSMarc-André Lureau 
218df85a78bSMarc-André Lureau     if ((d->prod[m] - d->cons[m]) < MUX_BUFFER_SIZE) {
219df85a78bSMarc-André Lureau         return 1;
220df85a78bSMarc-André Lureau     }
221df85a78bSMarc-André Lureau 
222df85a78bSMarc-André Lureau     if (be && be->chr_can_read) {
223df85a78bSMarc-André Lureau         return be->chr_can_read(be->opaque);
224df85a78bSMarc-André Lureau     }
225df85a78bSMarc-André Lureau 
226df85a78bSMarc-André Lureau     return 0;
227df85a78bSMarc-André Lureau }
228df85a78bSMarc-André Lureau 
mux_chr_read(void * opaque,const uint8_t * buf,int size)229df85a78bSMarc-André Lureau static void mux_chr_read(void *opaque, const uint8_t *buf, int size)
230df85a78bSMarc-André Lureau {
231df85a78bSMarc-André Lureau     Chardev *chr = CHARDEV(opaque);
232df85a78bSMarc-André Lureau     MuxChardev *d = MUX_CHARDEV(opaque);
233df85a78bSMarc-André Lureau     int m = d->focus;
234df85a78bSMarc-André Lureau     CharBackend *be = d->backends[m];
235df85a78bSMarc-André Lureau     int i;
236df85a78bSMarc-André Lureau 
237df85a78bSMarc-André Lureau     mux_chr_accept_input(opaque);
238df85a78bSMarc-André Lureau 
239df85a78bSMarc-André Lureau     for (i = 0; i < size; i++)
240df85a78bSMarc-André Lureau         if (mux_proc_byte(chr, d, buf[i])) {
241df85a78bSMarc-André Lureau             if (d->prod[m] == d->cons[m] &&
242df85a78bSMarc-André Lureau                 be && be->chr_can_read &&
243df85a78bSMarc-André Lureau                 be->chr_can_read(be->opaque)) {
244df85a78bSMarc-André Lureau                 be->chr_read(be->opaque, &buf[i], 1);
245df85a78bSMarc-André Lureau             } else {
246df85a78bSMarc-André Lureau                 d->buffer[m][d->prod[m]++ & MUX_BUFFER_MASK] = buf[i];
247df85a78bSMarc-André Lureau             }
248df85a78bSMarc-André Lureau         }
249df85a78bSMarc-André Lureau }
250df85a78bSMarc-André Lureau 
mux_chr_send_all_event(Chardev * chr,QEMUChrEvent event)2512fa9044aSPaolo Bonzini void mux_chr_send_all_event(Chardev *chr, QEMUChrEvent event)
252df85a78bSMarc-André Lureau {
253bed3bb9bSMarc-André Lureau     MuxChardev *d = MUX_CHARDEV(chr);
254005b6d51SRoman Penyaev     int bit;
255df85a78bSMarc-André Lureau 
2565a1ee607SPaolo Bonzini     if (!muxes_opened) {
257df85a78bSMarc-André Lureau         return;
258df85a78bSMarc-André Lureau     }
259df85a78bSMarc-André Lureau 
260df85a78bSMarc-André Lureau     /* Send the event to all registered listeners */
261005b6d51SRoman Penyaev     bit = -1;
262005b6d51SRoman Penyaev     while ((bit = find_next_bit(&d->mux_bitset, MAX_MUX, bit + 1)) < MAX_MUX) {
263005b6d51SRoman Penyaev         mux_chr_send_event(d, bit, event);
264df85a78bSMarc-André Lureau     }
265df85a78bSMarc-André Lureau }
266df85a78bSMarc-André Lureau 
mux_chr_event(void * opaque,QEMUChrEvent event)267083b266fSPhilippe Mathieu-Daudé static void mux_chr_event(void *opaque, QEMUChrEvent event)
268bed3bb9bSMarc-André Lureau {
269bed3bb9bSMarc-André Lureau     mux_chr_send_all_event(CHARDEV(opaque), event);
270bed3bb9bSMarc-André Lureau }
271bed3bb9bSMarc-André Lureau 
mux_chr_add_watch(Chardev * s,GIOCondition cond)272df85a78bSMarc-André Lureau static GSource *mux_chr_add_watch(Chardev *s, GIOCondition cond)
273df85a78bSMarc-André Lureau {
274df85a78bSMarc-André Lureau     MuxChardev *d = MUX_CHARDEV(s);
275df85a78bSMarc-André Lureau     Chardev *chr = qemu_chr_fe_get_driver(&d->chr);
276df85a78bSMarc-André Lureau     ChardevClass *cc = CHARDEV_GET_CLASS(chr);
277df85a78bSMarc-André Lureau 
278df85a78bSMarc-André Lureau     if (!cc->chr_add_watch) {
279df85a78bSMarc-André Lureau         return NULL;
280df85a78bSMarc-André Lureau     }
281df85a78bSMarc-André Lureau 
282df85a78bSMarc-André Lureau     return cc->chr_add_watch(chr, cond);
283df85a78bSMarc-André Lureau }
284df85a78bSMarc-André Lureau 
char_mux_finalize(Object * obj)285df85a78bSMarc-André Lureau static void char_mux_finalize(Object *obj)
286df85a78bSMarc-André Lureau {
287df85a78bSMarc-André Lureau     MuxChardev *d = MUX_CHARDEV(obj);
288005b6d51SRoman Penyaev     int bit;
289df85a78bSMarc-André Lureau 
290005b6d51SRoman Penyaev     bit = -1;
291005b6d51SRoman Penyaev     while ((bit = find_next_bit(&d->mux_bitset, MAX_MUX, bit + 1)) < MAX_MUX) {
292005b6d51SRoman Penyaev         CharBackend *be = d->backends[bit];
293df85a78bSMarc-André Lureau         be->chr = NULL;
294327993f1SRoman Penyaev         d->backends[bit] = NULL;
295df85a78bSMarc-André Lureau     }
296327993f1SRoman Penyaev     d->mux_bitset = 0;
2971ce2610cSMarc-André Lureau     qemu_chr_fe_deinit(&d->chr, false);
298df85a78bSMarc-André Lureau }
299df85a78bSMarc-André Lureau 
mux_chr_update_read_handlers(Chardev * chr)3003d9e2322SMarc-André Lureau static void mux_chr_update_read_handlers(Chardev *chr)
301df85a78bSMarc-André Lureau {
302df85a78bSMarc-André Lureau     MuxChardev *d = MUX_CHARDEV(chr);
303df85a78bSMarc-André Lureau 
304df85a78bSMarc-André Lureau     /* Fix up the real driver with mux routines */
3057a9657efSArtem Pisarenko     qemu_chr_fe_set_handlers_full(&d->chr,
306df85a78bSMarc-André Lureau                                   mux_chr_can_read,
307df85a78bSMarc-André Lureau                                   mux_chr_read,
308df85a78bSMarc-André Lureau                                   mux_chr_event,
30981517ba3SAnton Nefedov                                   NULL,
310df85a78bSMarc-André Lureau                                   chr,
3113d9e2322SMarc-André Lureau                                   chr->gcontext, true, false);
312df85a78bSMarc-André Lureau }
313df85a78bSMarc-André Lureau 
mux_chr_attach_frontend(MuxChardev * d,CharBackend * b,unsigned int * tag,Error ** errp)314709a4cabSRoman Penyaev bool mux_chr_attach_frontend(MuxChardev *d, CharBackend *b,
315709a4cabSRoman Penyaev                              unsigned int *tag, Error **errp)
316709a4cabSRoman Penyaev {
317005b6d51SRoman Penyaev     unsigned int bit;
318005b6d51SRoman Penyaev 
319188df56eSRoman Penyaev     QEMU_BUILD_BUG_ON(MAX_MUX > (sizeof(d->mux_bitset) * BITS_PER_BYTE));
320188df56eSRoman Penyaev 
321005b6d51SRoman Penyaev     bit = find_next_zero_bit(&d->mux_bitset, MAX_MUX, 0);
322005b6d51SRoman Penyaev     if (bit >= MAX_MUX) {
323709a4cabSRoman Penyaev         error_setg(errp,
324709a4cabSRoman Penyaev                    "too many uses of multiplexed chardev '%s'"
325709a4cabSRoman Penyaev                    " (maximum is " stringify(MAX_MUX) ")",
326709a4cabSRoman Penyaev                    d->parent.label);
327709a4cabSRoman Penyaev         return false;
328709a4cabSRoman Penyaev     }
329709a4cabSRoman Penyaev 
330188df56eSRoman Penyaev     d->mux_bitset |= (1ul << bit);
331005b6d51SRoman Penyaev     d->backends[bit] = b;
332005b6d51SRoman Penyaev     *tag = bit;
333709a4cabSRoman Penyaev 
334709a4cabSRoman Penyaev     return true;
335709a4cabSRoman Penyaev }
336709a4cabSRoman Penyaev 
mux_chr_detach_frontend(MuxChardev * d,unsigned int tag)337327993f1SRoman Penyaev bool mux_chr_detach_frontend(MuxChardev *d, unsigned int tag)
338327993f1SRoman Penyaev {
339*e6214fd6SRoman Penyaev     if (!(d->mux_bitset & (1ul << tag))) {
340327993f1SRoman Penyaev         return false;
341327993f1SRoman Penyaev     }
342327993f1SRoman Penyaev 
343*e6214fd6SRoman Penyaev     d->mux_bitset &= ~(1ul << tag);
344*e6214fd6SRoman Penyaev     d->backends[tag] = NULL;
345327993f1SRoman Penyaev 
346327993f1SRoman Penyaev     return true;
347327993f1SRoman Penyaev }
348327993f1SRoman Penyaev 
mux_set_focus(Chardev * chr,unsigned int focus)349c64f0bc1SRoman Penyaev void mux_set_focus(Chardev *chr, unsigned int focus)
350df85a78bSMarc-André Lureau {
351df85a78bSMarc-André Lureau     MuxChardev *d = MUX_CHARDEV(chr);
352df85a78bSMarc-André Lureau 
353*e6214fd6SRoman Penyaev     assert(d->mux_bitset & (1ul << focus));
354df85a78bSMarc-André Lureau 
355df85a78bSMarc-André Lureau     if (d->focus != -1) {
356df85a78bSMarc-André Lureau         mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT);
357df85a78bSMarc-André Lureau     }
358df85a78bSMarc-André Lureau 
359df85a78bSMarc-André Lureau     d->focus = focus;
360eeaa6715SMarc-André Lureau     chr->be = d->backends[focus];
361df85a78bSMarc-André Lureau     mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN);
362df85a78bSMarc-André Lureau }
363df85a78bSMarc-André Lureau 
qemu_chr_open_mux(Chardev * chr,ChardevBackend * backend,bool * be_opened,Error ** errp)364df85a78bSMarc-André Lureau static void qemu_chr_open_mux(Chardev *chr,
365df85a78bSMarc-André Lureau                               ChardevBackend *backend,
366df85a78bSMarc-André Lureau                               bool *be_opened,
367df85a78bSMarc-André Lureau                               Error **errp)
368df85a78bSMarc-André Lureau {
369df85a78bSMarc-André Lureau     ChardevMux *mux = backend->u.mux.data;
370df85a78bSMarc-André Lureau     Chardev *drv;
371df85a78bSMarc-André Lureau     MuxChardev *d = MUX_CHARDEV(chr);
372df85a78bSMarc-André Lureau 
373df85a78bSMarc-André Lureau     drv = qemu_chr_find(mux->chardev);
374df85a78bSMarc-André Lureau     if (drv == NULL) {
375df85a78bSMarc-André Lureau         error_setg(errp, "mux: base chardev %s not found", mux->chardev);
376df85a78bSMarc-André Lureau         return;
377df85a78bSMarc-André Lureau     }
378df85a78bSMarc-André Lureau 
379df85a78bSMarc-André Lureau     d->focus = -1;
380df85a78bSMarc-André Lureau     /* only default to opened state if we've realized the initial
381df85a78bSMarc-André Lureau      * set of muxes
382df85a78bSMarc-André Lureau      */
3835a1ee607SPaolo Bonzini     *be_opened = muxes_opened;
384df85a78bSMarc-André Lureau     qemu_chr_fe_init(&d->chr, drv, errp);
385df85a78bSMarc-André Lureau }
386df85a78bSMarc-André Lureau 
qemu_chr_parse_mux(QemuOpts * opts,ChardevBackend * backend,Error ** errp)387df85a78bSMarc-André Lureau static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend,
388df85a78bSMarc-André Lureau                                Error **errp)
389df85a78bSMarc-André Lureau {
390df85a78bSMarc-André Lureau     const char *chardev = qemu_opt_get(opts, "chardev");
391df85a78bSMarc-André Lureau     ChardevMux *mux;
392df85a78bSMarc-André Lureau 
393df85a78bSMarc-André Lureau     if (chardev == NULL) {
394df85a78bSMarc-André Lureau         error_setg(errp, "chardev: mux: no chardev given");
395df85a78bSMarc-André Lureau         return;
396df85a78bSMarc-André Lureau     }
397df85a78bSMarc-André Lureau     backend->type = CHARDEV_BACKEND_KIND_MUX;
398df85a78bSMarc-André Lureau     mux = backend->u.mux.data = g_new0(ChardevMux, 1);
399df85a78bSMarc-André Lureau     qemu_chr_parse_common(opts, qapi_ChardevMux_base(mux));
400df85a78bSMarc-André Lureau     mux->chardev = g_strdup(chardev);
401df85a78bSMarc-André Lureau }
402df85a78bSMarc-André Lureau 
403c7278b43SPeter Xu /**
404c7278b43SPeter Xu  * Called after processing of default and command-line-specified
405c7278b43SPeter Xu  * chardevs to deliver CHR_EVENT_OPENED events to any FEs attached
406c7278b43SPeter Xu  * to a mux chardev. This is done here to ensure that
407c7278b43SPeter Xu  * output/prompts/banners are only displayed for the FE that has
408c7278b43SPeter Xu  * focus when initial command-line processing/machine init is
409c7278b43SPeter Xu  * completed.
410c7278b43SPeter Xu  *
411c7278b43SPeter Xu  * After this point, any new FE attached to any new or existing
412c7278b43SPeter Xu  * mux will receive CHR_EVENT_OPENED notifications for the BE
413c7278b43SPeter Xu  * immediately.
414c7278b43SPeter Xu  */
open_muxes(Chardev * chr)4155a1ee607SPaolo Bonzini static void open_muxes(Chardev *chr)
416c7278b43SPeter Xu {
417c7278b43SPeter Xu     /* send OPENED to all already-attached FEs */
418c7278b43SPeter Xu     mux_chr_send_all_event(chr, CHR_EVENT_OPENED);
4195a1ee607SPaolo Bonzini 
420c7278b43SPeter Xu     /*
421c7278b43SPeter Xu      * mark mux as OPENED so any new FEs will immediately receive
422c7278b43SPeter Xu      * OPENED event
423c7278b43SPeter Xu      */
4247a9657efSArtem Pisarenko     chr->be_open = 1;
4255a1ee607SPaolo Bonzini }
4265a1ee607SPaolo Bonzini 
suspend_mux_open(void)4275a1ee607SPaolo Bonzini void suspend_mux_open(void)
4285a1ee607SPaolo Bonzini {
4295a1ee607SPaolo Bonzini     muxes_opened = false;
4305a1ee607SPaolo Bonzini }
4315a1ee607SPaolo Bonzini 
chardev_options_parsed_cb(Object * child,void * opaque)4325a1ee607SPaolo Bonzini static int chardev_options_parsed_cb(Object *child, void *opaque)
4335a1ee607SPaolo Bonzini {
4345a1ee607SPaolo Bonzini     Chardev *chr = (Chardev *)child;
4355a1ee607SPaolo Bonzini 
4365eed493dSMarc-André Lureau     if (!chr->be_open && CHARDEV_IS_MUX(chr)) {
4375eed493dSMarc-André Lureau         open_muxes(chr);
4385a1ee607SPaolo Bonzini     }
439c7278b43SPeter Xu 
440c7278b43SPeter Xu     return 0;
441c7278b43SPeter Xu }
442c7278b43SPeter Xu 
resume_mux_open(void)4435a1ee607SPaolo Bonzini void resume_mux_open(void)
4445a1ee607SPaolo Bonzini {
4455a1ee607SPaolo Bonzini     muxes_opened = true;
4465a1ee607SPaolo Bonzini     object_child_foreach(get_chardevs_root(),
4475a1ee607SPaolo Bonzini                          chardev_options_parsed_cb, NULL);
4485a1ee607SPaolo Bonzini }
4495a1ee607SPaolo Bonzini 
char_mux_class_init(ObjectClass * oc,void * data)450df85a78bSMarc-André Lureau static void char_mux_class_init(ObjectClass *oc, void *data)
451df85a78bSMarc-André Lureau {
452df85a78bSMarc-André Lureau     ChardevClass *cc = CHARDEV_CLASS(oc);
453df85a78bSMarc-André Lureau 
454df85a78bSMarc-André Lureau     cc->parse = qemu_chr_parse_mux;
455df85a78bSMarc-André Lureau     cc->open = qemu_chr_open_mux;
456df85a78bSMarc-André Lureau     cc->chr_write = mux_chr_write;
457df85a78bSMarc-André Lureau     cc->chr_accept_input = mux_chr_accept_input;
458df85a78bSMarc-André Lureau     cc->chr_add_watch = mux_chr_add_watch;
459d09c4a47SMarc-André Lureau     cc->chr_be_event = mux_chr_be_event;
4603d9e2322SMarc-André Lureau     cc->chr_update_read_handler = mux_chr_update_read_handlers;
461df85a78bSMarc-André Lureau }
462df85a78bSMarc-André Lureau 
463df85a78bSMarc-André Lureau static const TypeInfo char_mux_type_info = {
464df85a78bSMarc-André Lureau     .name = TYPE_CHARDEV_MUX,
465df85a78bSMarc-André Lureau     .parent = TYPE_CHARDEV,
466df85a78bSMarc-André Lureau     .class_init = char_mux_class_init,
467df85a78bSMarc-André Lureau     .instance_size = sizeof(MuxChardev),
468df85a78bSMarc-André Lureau     .instance_finalize = char_mux_finalize,
469df85a78bSMarc-André Lureau };
470df85a78bSMarc-André Lureau 
register_types(void)471df85a78bSMarc-André Lureau static void register_types(void)
472df85a78bSMarc-André Lureau {
473df85a78bSMarc-André Lureau     type_register_static(&char_mux_type_info);
474df85a78bSMarc-André Lureau }
475df85a78bSMarc-André Lureau 
476df85a78bSMarc-André Lureau type_init(register_types);
477