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