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