xref: /openbmc/qemu/chardev/char-fe.c (revision fb0bc835e56b894cbc7236294921e5393c786ad8)
1 /*
2  * QEMU System Emulator
3  *
4  * Copyright (c) 2003-2008 Fabrice Bellard
5  *
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:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
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
22  * THE SOFTWARE.
23  */
24 #include "qemu/osdep.h"
25 #include "qemu/error-report.h"
26 #include "qapi/error.h"
27 #include "qapi/qmp/qerror.h"
28 #include "qapi-visit.h"
29 #include "sysemu/replay.h"
30 
31 #include "chardev/char-fe.h"
32 #include "chardev/char-io.h"
33 #include "chardev/char-mux.h"
34 
35 int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len)
36 {
37     Chardev *s = be->chr;
38 
39     if (!s) {
40         return 0;
41     }
42 
43     return qemu_chr_write(s, buf, len, false);
44 }
45 
46 int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len)
47 {
48     Chardev *s = be->chr;
49 
50     if (!s) {
51         return 0;
52     }
53 
54     return qemu_chr_write(s, buf, len, true);
55 }
56 
57 int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len)
58 {
59     Chardev *s = be->chr;
60     int offset = 0, counter = 10;
61     int res;
62 
63     if (!s || !CHARDEV_GET_CLASS(s)->chr_sync_read) {
64         return 0;
65     }
66 
67     if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_PLAY) {
68         return replay_char_read_all_load(buf);
69     }
70 
71     while (offset < len) {
72     retry:
73         res = CHARDEV_GET_CLASS(s)->chr_sync_read(s, buf + offset,
74                                                   len - offset);
75         if (res == -1 && errno == EAGAIN) {
76             g_usleep(100);
77             goto retry;
78         }
79 
80         if (res == 0) {
81             break;
82         }
83 
84         if (res < 0) {
85             if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) {
86                 replay_char_read_all_save_error(res);
87             }
88             return res;
89         }
90 
91         offset += res;
92 
93         if (!counter--) {
94             break;
95         }
96     }
97 
98     if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) {
99         replay_char_read_all_save_buf(buf, offset);
100     }
101     return offset;
102 }
103 
104 int qemu_chr_fe_ioctl(CharBackend *be, int cmd, void *arg)
105 {
106     Chardev *s = be->chr;
107     int res;
108 
109     if (!s || !CHARDEV_GET_CLASS(s)->chr_ioctl || qemu_chr_replay(s)) {
110         res = -ENOTSUP;
111     } else {
112         res = CHARDEV_GET_CLASS(s)->chr_ioctl(s, cmd, arg);
113     }
114 
115     return res;
116 }
117 
118 int qemu_chr_fe_get_msgfd(CharBackend *be)
119 {
120     Chardev *s = be->chr;
121     int fd;
122     int res = (qemu_chr_fe_get_msgfds(be, &fd, 1) == 1) ? fd : -1;
123     if (s && qemu_chr_replay(s)) {
124         error_report("Replay: get msgfd is not supported "
125                      "for serial devices yet");
126         exit(1);
127     }
128     return res;
129 }
130 
131 int qemu_chr_fe_get_msgfds(CharBackend *be, int *fds, int len)
132 {
133     Chardev *s = be->chr;
134 
135     if (!s) {
136         return -1;
137     }
138 
139     return CHARDEV_GET_CLASS(s)->get_msgfds ?
140         CHARDEV_GET_CLASS(s)->get_msgfds(s, fds, len) : -1;
141 }
142 
143 int qemu_chr_fe_set_msgfds(CharBackend *be, int *fds, int num)
144 {
145     Chardev *s = be->chr;
146 
147     if (!s) {
148         return -1;
149     }
150 
151     return CHARDEV_GET_CLASS(s)->set_msgfds ?
152         CHARDEV_GET_CLASS(s)->set_msgfds(s, fds, num) : -1;
153 }
154 
155 void qemu_chr_fe_accept_input(CharBackend *be)
156 {
157     Chardev *s = be->chr;
158 
159     if (!s) {
160         return;
161     }
162 
163     if (CHARDEV_GET_CLASS(s)->chr_accept_input) {
164         CHARDEV_GET_CLASS(s)->chr_accept_input(s);
165     }
166     qemu_notify_event();
167 }
168 
169 void qemu_chr_fe_printf(CharBackend *be, const char *fmt, ...)
170 {
171     char buf[CHR_READ_BUF_LEN];
172     va_list ap;
173     va_start(ap, fmt);
174     vsnprintf(buf, sizeof(buf), fmt, ap);
175     /* XXX this blocks entire thread. Rewrite to use
176      * qemu_chr_fe_write and background I/O callbacks */
177     qemu_chr_fe_write_all(be, (uint8_t *)buf, strlen(buf));
178     va_end(ap);
179 }
180 
181 Chardev *qemu_chr_fe_get_driver(CharBackend *be)
182 {
183     /* this is unsafe for the users that support chardev hotswap */
184     assert(be->chr_be_change == NULL);
185     return be->chr;
186 }
187 
188 bool qemu_chr_fe_backend_connected(CharBackend *be)
189 {
190     return !!be->chr;
191 }
192 
193 bool qemu_chr_fe_backend_open(CharBackend *be)
194 {
195     return be->chr && be->chr->be_open;
196 }
197 
198 bool qemu_chr_fe_init(CharBackend *b, Chardev *s, Error **errp)
199 {
200     int tag = 0;
201 
202     if (CHARDEV_IS_MUX(s)) {
203         MuxChardev *d = MUX_CHARDEV(s);
204 
205         if (d->mux_cnt >= MAX_MUX) {
206             goto unavailable;
207         }
208 
209         d->backends[d->mux_cnt] = b;
210         tag = d->mux_cnt++;
211     } else if (s->be) {
212         goto unavailable;
213     } else {
214         s->be = b;
215     }
216 
217     b->fe_open = false;
218     b->tag = tag;
219     b->chr = s;
220     return true;
221 
222 unavailable:
223     error_setg(errp, QERR_DEVICE_IN_USE, s->label);
224     return false;
225 }
226 
227 void qemu_chr_fe_deinit(CharBackend *b, bool del)
228 {
229     assert(b);
230 
231     if (b->chr) {
232         qemu_chr_fe_set_handlers(b, NULL, NULL, NULL, NULL, NULL, NULL, true);
233         if (b->chr->be == b) {
234             b->chr->be = NULL;
235         }
236         if (CHARDEV_IS_MUX(b->chr)) {
237             MuxChardev *d = MUX_CHARDEV(b->chr);
238             d->backends[b->tag] = NULL;
239         }
240         if (del) {
241             object_unparent(OBJECT(b->chr));
242         }
243         b->chr = NULL;
244     }
245 }
246 
247 void qemu_chr_fe_set_handlers(CharBackend *b,
248                               IOCanReadHandler *fd_can_read,
249                               IOReadHandler *fd_read,
250                               IOEventHandler *fd_event,
251                               BackendChangeHandler *be_change,
252                               void *opaque,
253                               GMainContext *context,
254                               bool set_open)
255 {
256     Chardev *s;
257     int fe_open;
258 
259     s = b->chr;
260     if (!s) {
261         return;
262     }
263 
264     if (!opaque && !fd_can_read && !fd_read && !fd_event) {
265         fe_open = 0;
266         remove_fd_in_watch(s);
267     } else {
268         fe_open = 1;
269     }
270     b->chr_can_read = fd_can_read;
271     b->chr_read = fd_read;
272     b->chr_event = fd_event;
273     b->chr_be_change = be_change;
274     b->opaque = opaque;
275 
276     qemu_chr_be_update_read_handlers(s, context);
277 
278     if (set_open) {
279         qemu_chr_fe_set_open(b, fe_open);
280     }
281 
282     if (fe_open) {
283         qemu_chr_fe_take_focus(b);
284         /* We're connecting to an already opened device, so let's make sure we
285            also get the open event */
286         if (s->be_open) {
287             qemu_chr_be_event(s, CHR_EVENT_OPENED);
288         }
289     }
290 
291     if (CHARDEV_IS_MUX(s)) {
292         mux_chr_set_handlers(s, context);
293     }
294 }
295 
296 void qemu_chr_fe_take_focus(CharBackend *b)
297 {
298     if (!b->chr) {
299         return;
300     }
301 
302     if (CHARDEV_IS_MUX(b->chr)) {
303         mux_set_focus(b->chr, b->tag);
304     }
305 }
306 
307 int qemu_chr_fe_wait_connected(CharBackend *be, Error **errp)
308 {
309     if (!be->chr) {
310         error_setg(errp, "missing associated backend");
311         return -1;
312     }
313 
314     return qemu_chr_wait_connected(be->chr, errp);
315 }
316 
317 void qemu_chr_fe_set_echo(CharBackend *be, bool echo)
318 {
319     Chardev *chr = be->chr;
320 
321     if (chr && CHARDEV_GET_CLASS(chr)->chr_set_echo) {
322         CHARDEV_GET_CLASS(chr)->chr_set_echo(chr, echo);
323     }
324 }
325 
326 void qemu_chr_fe_set_open(CharBackend *be, int fe_open)
327 {
328     Chardev *chr = be->chr;
329 
330     if (!chr) {
331         return;
332     }
333 
334     if (be->fe_open == fe_open) {
335         return;
336     }
337     be->fe_open = fe_open;
338     if (CHARDEV_GET_CLASS(chr)->chr_set_fe_open) {
339         CHARDEV_GET_CLASS(chr)->chr_set_fe_open(chr, fe_open);
340     }
341 }
342 
343 guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond,
344                             GIOFunc func, void *user_data)
345 {
346     Chardev *s = be->chr;
347     GSource *src;
348     guint tag;
349 
350     if (!s || CHARDEV_GET_CLASS(s)->chr_add_watch == NULL) {
351         return 0;
352     }
353 
354     src = CHARDEV_GET_CLASS(s)->chr_add_watch(s, cond);
355     if (!src) {
356         return 0;
357     }
358 
359     g_source_set_callback(src, (GSourceFunc)func, user_data, NULL);
360     tag = g_source_attach(src, s->gcontext);
361     g_source_unref(src);
362 
363     return tag;
364 }
365 
366 void qemu_chr_fe_disconnect(CharBackend *be)
367 {
368     Chardev *chr = be->chr;
369 
370     if (chr && CHARDEV_GET_CLASS(chr)->chr_disconnect) {
371         CHARDEV_GET_CLASS(chr)->chr_disconnect(chr);
372     }
373 }
374