xref: /openbmc/qemu/chardev/char-fd.c (revision aa96ab7c9df59c615ca82b49c9062819e0a1c287)
1894593afSMarc-André Lureau /*
2894593afSMarc-André Lureau  * QEMU System Emulator
3894593afSMarc-André Lureau  *
4894593afSMarc-André Lureau  * Copyright (c) 2003-2008 Fabrice Bellard
5894593afSMarc-André Lureau  *
6894593afSMarc-André Lureau  * Permission is hereby granted, free of charge, to any person obtaining a copy
7894593afSMarc-André Lureau  * of this software and associated documentation files (the "Software"), to deal
8894593afSMarc-André Lureau  * in the Software without restriction, including without limitation the rights
9894593afSMarc-André Lureau  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10894593afSMarc-André Lureau  * copies of the Software, and to permit persons to whom the Software is
11894593afSMarc-André Lureau  * furnished to do so, subject to the following conditions:
12894593afSMarc-André Lureau  *
13894593afSMarc-André Lureau  * The above copyright notice and this permission notice shall be included in
14894593afSMarc-André Lureau  * all copies or substantial portions of the Software.
15894593afSMarc-André Lureau  *
16894593afSMarc-André Lureau  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17894593afSMarc-André Lureau  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18894593afSMarc-André Lureau  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19894593afSMarc-André Lureau  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20894593afSMarc-André Lureau  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21894593afSMarc-André Lureau  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22894593afSMarc-André Lureau  * THE SOFTWARE.
23894593afSMarc-André Lureau  */
240b8fa32fSMarkus Armbruster 
25894593afSMarc-André Lureau #include "qemu/osdep.h"
260b8fa32fSMarkus Armbruster #include "qemu/module.h"
27894593afSMarc-André Lureau #include "qemu/sockets.h"
28894593afSMarc-André Lureau #include "qapi/error.h"
298228e353SMarc-André Lureau #include "chardev/char.h"
30bb2b058fSMarc-André Lureau #include "chardev/char-fe.h"
31894593afSMarc-André Lureau #include "io/channel-file.h"
32894593afSMarc-André Lureau 
338228e353SMarc-André Lureau #include "chardev/char-fd.h"
348228e353SMarc-André Lureau #include "chardev/char-io.h"
35894593afSMarc-André Lureau 
36894593afSMarc-André Lureau /* Called with chr_write_lock held.  */
fd_chr_write(Chardev * chr,const uint8_t * buf,int len)37894593afSMarc-André Lureau static int fd_chr_write(Chardev *chr, const uint8_t *buf, int len)
38894593afSMarc-André Lureau {
39894593afSMarc-André Lureau     FDChardev *s = FD_CHARDEV(chr);
40894593afSMarc-André Lureau 
4146fe3ff6SMarc-André Lureau     if (!s->ioc_out) {
4246fe3ff6SMarc-André Lureau         return -1;
4346fe3ff6SMarc-André Lureau     }
4446fe3ff6SMarc-André Lureau 
45894593afSMarc-André Lureau     return io_channel_send(s->ioc_out, buf, len);
46894593afSMarc-André Lureau }
47894593afSMarc-André Lureau 
fd_chr_read(QIOChannel * chan,GIOCondition cond,void * opaque)48894593afSMarc-André Lureau static gboolean fd_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
49894593afSMarc-André Lureau {
50894593afSMarc-André Lureau     Chardev *chr = CHARDEV(opaque);
51894593afSMarc-André Lureau     FDChardev *s = FD_CHARDEV(opaque);
52894593afSMarc-André Lureau     int len;
53894593afSMarc-André Lureau     uint8_t buf[CHR_READ_BUF_LEN];
54894593afSMarc-André Lureau     ssize_t ret;
55894593afSMarc-André Lureau 
56894593afSMarc-André Lureau     len = sizeof(buf);
57894593afSMarc-André Lureau     if (len > s->max_size) {
58894593afSMarc-André Lureau         len = s->max_size;
59894593afSMarc-André Lureau     }
60894593afSMarc-André Lureau     if (len == 0) {
61894593afSMarc-André Lureau         return TRUE;
62894593afSMarc-André Lureau     }
63894593afSMarc-André Lureau 
64894593afSMarc-André Lureau     ret = qio_channel_read(
65894593afSMarc-André Lureau         chan, (gchar *)buf, len, NULL);
66894593afSMarc-André Lureau     if (ret == 0) {
67b19456ddSzhanghailiang         remove_fd_in_watch(chr);
68894593afSMarc-André Lureau         qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
69894593afSMarc-André Lureau         return FALSE;
70894593afSMarc-André Lureau     }
71894593afSMarc-André Lureau     if (ret > 0) {
72894593afSMarc-André Lureau         qemu_chr_be_write(chr, buf, ret);
73894593afSMarc-André Lureau     }
74894593afSMarc-André Lureau 
75894593afSMarc-André Lureau     return TRUE;
76894593afSMarc-André Lureau }
77894593afSMarc-André Lureau 
fd_chr_read_poll(void * opaque)78894593afSMarc-André Lureau static int fd_chr_read_poll(void *opaque)
79894593afSMarc-André Lureau {
80894593afSMarc-André Lureau     Chardev *chr = CHARDEV(opaque);
81894593afSMarc-André Lureau     FDChardev *s = FD_CHARDEV(opaque);
82894593afSMarc-André Lureau 
83894593afSMarc-André Lureau     s->max_size = qemu_chr_be_can_write(chr);
84894593afSMarc-André Lureau     return s->max_size;
85894593afSMarc-André Lureau }
86894593afSMarc-André Lureau 
87bb2b058fSMarc-André Lureau typedef struct FDSource {
88bb2b058fSMarc-André Lureau     GSource parent;
89bb2b058fSMarc-André Lureau 
90bb2b058fSMarc-André Lureau     GIOCondition cond;
91bb2b058fSMarc-André Lureau } FDSource;
92bb2b058fSMarc-André Lureau 
93bb2b058fSMarc-André Lureau static gboolean
fd_source_prepare(GSource * source,gint * timeout_)94bb2b058fSMarc-André Lureau fd_source_prepare(GSource *source,
95bb2b058fSMarc-André Lureau                   gint *timeout_)
96bb2b058fSMarc-André Lureau {
97bb2b058fSMarc-André Lureau     FDSource *src = (FDSource *)source;
98bb2b058fSMarc-André Lureau 
99bb2b058fSMarc-André Lureau     return src->cond != 0;
100bb2b058fSMarc-André Lureau }
101bb2b058fSMarc-André Lureau 
102bb2b058fSMarc-André Lureau static gboolean
fd_source_check(GSource * source)103bb2b058fSMarc-André Lureau fd_source_check(GSource *source)
104bb2b058fSMarc-André Lureau {
105bb2b058fSMarc-André Lureau     FDSource *src = (FDSource *)source;
106bb2b058fSMarc-André Lureau 
107bb2b058fSMarc-André Lureau     return src->cond != 0;
108bb2b058fSMarc-André Lureau }
109bb2b058fSMarc-André Lureau 
110bb2b058fSMarc-André Lureau static gboolean
fd_source_dispatch(GSource * source,GSourceFunc callback,gpointer user_data)111bb2b058fSMarc-André Lureau fd_source_dispatch(GSource *source, GSourceFunc callback,
112bb2b058fSMarc-André Lureau                    gpointer user_data)
113bb2b058fSMarc-André Lureau {
114bb2b058fSMarc-André Lureau     FDSource *src = (FDSource *)source;
115bb2b058fSMarc-André Lureau     FEWatchFunc func = (FEWatchFunc)callback;
116bb2b058fSMarc-André Lureau     gboolean ret = G_SOURCE_CONTINUE;
117bb2b058fSMarc-André Lureau 
118bb2b058fSMarc-André Lureau     if (src->cond) {
119bb2b058fSMarc-André Lureau         ret = func(NULL, src->cond, user_data);
120bb2b058fSMarc-André Lureau         src->cond = 0;
121bb2b058fSMarc-André Lureau     }
122bb2b058fSMarc-André Lureau 
123bb2b058fSMarc-André Lureau     return ret;
124bb2b058fSMarc-André Lureau }
125bb2b058fSMarc-André Lureau 
126bb2b058fSMarc-André Lureau static GSourceFuncs fd_source_funcs = {
127bb2b058fSMarc-André Lureau   fd_source_prepare,
128bb2b058fSMarc-André Lureau   fd_source_check,
129bb2b058fSMarc-André Lureau   fd_source_dispatch,
130bb2b058fSMarc-André Lureau   NULL, NULL, NULL
131bb2b058fSMarc-André Lureau };
132bb2b058fSMarc-André Lureau 
fd_source_new(FDChardev * chr)133bb2b058fSMarc-André Lureau static GSource *fd_source_new(FDChardev *chr)
134bb2b058fSMarc-André Lureau {
135bb2b058fSMarc-André Lureau     return g_source_new(&fd_source_funcs, sizeof(FDSource));
136bb2b058fSMarc-André Lureau }
137bb2b058fSMarc-André Lureau 
child_func(GIOChannel * source,GIOCondition condition,gpointer data)138bb2b058fSMarc-André Lureau static gboolean child_func(GIOChannel *source,
139bb2b058fSMarc-André Lureau                            GIOCondition condition,
140bb2b058fSMarc-André Lureau                            gpointer data)
141bb2b058fSMarc-André Lureau {
142bb2b058fSMarc-André Lureau     FDSource *parent = data;
143bb2b058fSMarc-André Lureau 
144bb2b058fSMarc-André Lureau     parent->cond |= condition;
145bb2b058fSMarc-André Lureau 
146bb2b058fSMarc-André Lureau     return G_SOURCE_CONTINUE;
147bb2b058fSMarc-André Lureau }
148bb2b058fSMarc-André Lureau 
fd_chr_add_watch(Chardev * chr,GIOCondition cond)149894593afSMarc-André Lureau static GSource *fd_chr_add_watch(Chardev *chr, GIOCondition cond)
150894593afSMarc-André Lureau {
151894593afSMarc-André Lureau     FDChardev *s = FD_CHARDEV(chr);
152bb2b058fSMarc-André Lureau     g_autoptr(GSource) source = fd_source_new(s);
153bb2b058fSMarc-André Lureau 
154bb2b058fSMarc-André Lureau     if (s->ioc_out) {
155bb2b058fSMarc-André Lureau         g_autoptr(GSource) child = qio_channel_create_watch(s->ioc_out, cond & ~G_IO_IN);
156bb2b058fSMarc-André Lureau         g_source_set_callback(child, (GSourceFunc)child_func, source, NULL);
157bb2b058fSMarc-André Lureau         g_source_add_child_source(source, child);
158bb2b058fSMarc-André Lureau     }
159bb2b058fSMarc-André Lureau     if (s->ioc_in) {
160bb2b058fSMarc-André Lureau         g_autoptr(GSource) child = qio_channel_create_watch(s->ioc_in, cond & ~G_IO_OUT);
161bb2b058fSMarc-André Lureau         g_source_set_callback(child, (GSourceFunc)child_func, source, NULL);
162bb2b058fSMarc-André Lureau         g_source_add_child_source(source, child);
163bb2b058fSMarc-André Lureau     }
164bb2b058fSMarc-André Lureau 
165bb2b058fSMarc-André Lureau     return g_steal_pointer(&source);
166894593afSMarc-André Lureau }
167894593afSMarc-André Lureau 
fd_chr_update_read_handler(Chardev * chr)168bb86d05fSPeter Xu static void fd_chr_update_read_handler(Chardev *chr)
169894593afSMarc-André Lureau {
170894593afSMarc-André Lureau     FDChardev *s = FD_CHARDEV(chr);
171894593afSMarc-André Lureau 
172b19456ddSzhanghailiang     remove_fd_in_watch(chr);
173894593afSMarc-André Lureau     if (s->ioc_in) {
174b19456ddSzhanghailiang         chr->gsource = io_add_watch_poll(chr, s->ioc_in,
175894593afSMarc-André Lureau                                            fd_chr_read_poll,
176894593afSMarc-André Lureau                                            fd_chr_read, chr,
1776bbb6c06SPeter Xu                                            chr->gcontext);
178894593afSMarc-André Lureau     }
179894593afSMarc-André Lureau }
180894593afSMarc-André Lureau 
char_fd_finalize(Object * obj)181894593afSMarc-André Lureau static void char_fd_finalize(Object *obj)
182894593afSMarc-André Lureau {
183894593afSMarc-André Lureau     Chardev *chr = CHARDEV(obj);
184894593afSMarc-André Lureau     FDChardev *s = FD_CHARDEV(obj);
185894593afSMarc-André Lureau 
186b19456ddSzhanghailiang     remove_fd_in_watch(chr);
187894593afSMarc-André Lureau     if (s->ioc_in) {
188894593afSMarc-André Lureau         object_unref(OBJECT(s->ioc_in));
189894593afSMarc-André Lureau     }
190894593afSMarc-André Lureau     if (s->ioc_out) {
191894593afSMarc-André Lureau         object_unref(OBJECT(s->ioc_out));
192894593afSMarc-André Lureau     }
193894593afSMarc-André Lureau 
194894593afSMarc-André Lureau     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
195894593afSMarc-André Lureau }
196894593afSMarc-André Lureau 
qmp_chardev_open_file_source(char * src,int flags,Error ** errp)197894593afSMarc-André Lureau int qmp_chardev_open_file_source(char *src, int flags, Error **errp)
198894593afSMarc-André Lureau {
199894593afSMarc-André Lureau     int fd = -1;
200894593afSMarc-André Lureau 
201*8b6aa693SNikita Ivanov     fd = RETRY_ON_EINTR(qemu_open_old(src, flags, 0666));
202894593afSMarc-André Lureau     if (fd == -1) {
203894593afSMarc-André Lureau         error_setg_file_open(errp, errno, src);
204894593afSMarc-André Lureau     }
205894593afSMarc-André Lureau     return fd;
206894593afSMarc-André Lureau }
207894593afSMarc-André Lureau 
208894593afSMarc-André Lureau /* open a character device to a unix fd */
qemu_chr_open_fd(Chardev * chr,int fd_in,int fd_out)209894593afSMarc-André Lureau void qemu_chr_open_fd(Chardev *chr,
210894593afSMarc-André Lureau                       int fd_in, int fd_out)
211894593afSMarc-André Lureau {
212894593afSMarc-André Lureau     FDChardev *s = FD_CHARDEV(chr);
213733ba020SMarc-André Lureau     g_autofree char *name = NULL;
214733ba020SMarc-André Lureau 
215b84bb4dfSMarc-André Lureau     if (fd_out >= 0 && !g_unix_set_fd_nonblocking(fd_out, true, NULL)) {
216b84bb4dfSMarc-André Lureau         assert(!"Failed to set FD nonblocking");
217733ba020SMarc-André Lureau     }
218733ba020SMarc-André Lureau 
219733ba020SMarc-André Lureau     if (fd_out == fd_in && fd_in >= 0) {
220733ba020SMarc-André Lureau         s->ioc_in = QIO_CHANNEL(qio_channel_file_new_fd(fd_in));
221733ba020SMarc-André Lureau         name = g_strdup_printf("chardev-file-%s", chr->label);
222733ba020SMarc-André Lureau         qio_channel_set_name(QIO_CHANNEL(s->ioc_in), name);
223733ba020SMarc-André Lureau         s->ioc_out = QIO_CHANNEL(object_ref(s->ioc_in));
224733ba020SMarc-André Lureau         return;
225733ba020SMarc-André Lureau     }
226894593afSMarc-André Lureau 
22746fe3ff6SMarc-André Lureau     if (fd_in >= 0) {
228894593afSMarc-André Lureau         s->ioc_in = QIO_CHANNEL(qio_channel_file_new_fd(fd_in));
229894593afSMarc-André Lureau         name = g_strdup_printf("chardev-file-in-%s", chr->label);
230894593afSMarc-André Lureau         qio_channel_set_name(QIO_CHANNEL(s->ioc_in), name);
23146fe3ff6SMarc-André Lureau     }
232733ba020SMarc-André Lureau 
23346fe3ff6SMarc-André Lureau     if (fd_out >= 0) {
234894593afSMarc-André Lureau         s->ioc_out = QIO_CHANNEL(qio_channel_file_new_fd(fd_out));
235733ba020SMarc-André Lureau         g_free(name);
236894593afSMarc-André Lureau         name = g_strdup_printf("chardev-file-out-%s", chr->label);
237894593afSMarc-André Lureau         qio_channel_set_name(QIO_CHANNEL(s->ioc_out), name);
238894593afSMarc-André Lureau     }
23946fe3ff6SMarc-André Lureau }
240894593afSMarc-André Lureau 
char_fd_class_init(ObjectClass * oc,void * data)241894593afSMarc-André Lureau static void char_fd_class_init(ObjectClass *oc, void *data)
242894593afSMarc-André Lureau {
243894593afSMarc-André Lureau     ChardevClass *cc = CHARDEV_CLASS(oc);
244894593afSMarc-André Lureau 
245894593afSMarc-André Lureau     cc->chr_add_watch = fd_chr_add_watch;
246894593afSMarc-André Lureau     cc->chr_write = fd_chr_write;
247894593afSMarc-André Lureau     cc->chr_update_read_handler = fd_chr_update_read_handler;
248894593afSMarc-André Lureau }
249894593afSMarc-André Lureau 
250894593afSMarc-André Lureau static const TypeInfo char_fd_type_info = {
251894593afSMarc-André Lureau     .name = TYPE_CHARDEV_FD,
252894593afSMarc-André Lureau     .parent = TYPE_CHARDEV,
253894593afSMarc-André Lureau     .instance_size = sizeof(FDChardev),
254894593afSMarc-André Lureau     .instance_finalize = char_fd_finalize,
255894593afSMarc-André Lureau     .class_init = char_fd_class_init,
256894593afSMarc-André Lureau     .abstract = true,
257894593afSMarc-André Lureau };
258894593afSMarc-André Lureau 
register_types(void)259894593afSMarc-André Lureau static void register_types(void)
260894593afSMarc-André Lureau {
261894593afSMarc-André Lureau     type_register_static(&char_fd_type_info);
262894593afSMarc-André Lureau }
263894593afSMarc-André Lureau 
264894593afSMarc-André Lureau type_init(register_types);
265