df0951e97368bcc3ab79831c0cade60799b0ace1
[sdk/emulator/qemu.git] / hw / virtio-console.c
1 /*
2  * Virtio Console and Generic Serial Port Devices
3  *
4  * Copyright Red Hat, Inc. 2009, 2010
5  *
6  * Authors:
7  *  Amit Shah <amit.shah@redhat.com>
8  *
9  * This work is licensed under the terms of the GNU GPL, version 2.  See
10  * the COPYING file in the top-level directory.
11  */
12
13 #include "qemu-char.h"
14 #include "qemu/error-report.h"
15 #include "trace.h"
16 #include "virtio-serial.h"
17
18 typedef struct VirtConsole {
19     VirtIOSerialPort port;
20     CharDriverState *chr;
21 } VirtConsole;
22
23
24 /* Callback function that's called when the guest sends us data */
25 static ssize_t flush_buf(VirtIOSerialPort *port, const uint8_t *buf, size_t len)
26 {
27     VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port);
28     ssize_t ret;
29
30     if (!vcon->chr) {
31         /* If there's no backend, we can just say we consumed all data. */
32         return len;
33     }
34
35     ret = qemu_chr_fe_write(vcon->chr, buf, len);
36     trace_virtio_console_flush_buf(port->id, len, ret);
37
38     if (ret < 0) {
39         /*
40          * Ideally we'd get a better error code than just -1, but
41          * that's what the chardev interface gives us right now.  If
42          * we had a finer-grained message, like -EPIPE, we could close
43          * this connection.  Absent such error messages, the most we
44          * can do is to return 0 here.
45          *
46          * This will prevent stray -1 values to go to
47          * virtio-serial-bus.c and cause abort()s in
48          * do_flush_queued_data().
49          */
50         ret = 0;
51     }
52     return ret;
53 }
54
55 /* Callback function that's called when the guest opens the port */
56 static void guest_open(VirtIOSerialPort *port)
57 {
58     VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port);
59
60     if (!vcon->chr) {
61         return;
62     }
63     qemu_chr_fe_open(vcon->chr);
64 }
65
66 /* Callback function that's called when the guest closes the port */
67 static void guest_close(VirtIOSerialPort *port)
68 {
69     VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port);
70
71     if (!vcon->chr) {
72         return;
73     }
74     qemu_chr_fe_close(vcon->chr);
75 }
76
77 /* Readiness of the guest to accept data on a port */
78 static int chr_can_read(void *opaque)
79 {
80     VirtConsole *vcon = opaque;
81
82     return virtio_serial_guest_ready(&vcon->port);
83 }
84
85 /* Send data from a char device over to the guest */
86 static void chr_read(void *opaque, const uint8_t *buf, int size)
87 {
88     VirtConsole *vcon = opaque;
89
90     trace_virtio_console_chr_read(vcon->port.id, size);
91     virtio_serial_write(&vcon->port, buf, size);
92 }
93
94 static void chr_event(void *opaque, int event)
95 {
96     VirtConsole *vcon = opaque;
97
98     trace_virtio_console_chr_event(vcon->port.id, event);
99     switch (event) {
100     case CHR_EVENT_OPENED:
101         virtio_serial_open(&vcon->port);
102         break;
103     case CHR_EVENT_CLOSED:
104         virtio_serial_close(&vcon->port);
105         break;
106     }
107 }
108
109 static int virtconsole_initfn(VirtIOSerialPort *port)
110 {
111     VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port);
112     VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(port);
113
114     if (port->id == 0 && !k->is_console) {
115         error_report("Port number 0 on virtio-serial devices reserved for virtconsole devices for backward compatibility.");
116         return -1;
117     }
118
119     if (vcon->chr) {
120         qemu_chr_add_handlers(vcon->chr, chr_can_read, chr_read, chr_event,
121                               vcon);
122     }
123
124     return 0;
125 }
126
127 static Property virtconsole_properties[] = {
128     DEFINE_PROP_CHR("chardev", VirtConsole, chr),
129     DEFINE_PROP_END_OF_LIST(),
130 };
131
132 static void virtconsole_class_init(ObjectClass *klass, void *data)
133 {
134     DeviceClass *dc = DEVICE_CLASS(klass);
135     VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_CLASS(klass);
136
137     k->is_console = true;
138     k->init = virtconsole_initfn;
139     k->have_data = flush_buf;
140     k->guest_open = guest_open;
141     k->guest_close = guest_close;
142     dc->props = virtconsole_properties;
143 }
144
145 static TypeInfo virtconsole_info = {
146     .name          = "virtconsole",
147     .parent        = TYPE_VIRTIO_SERIAL_PORT,
148     .instance_size = sizeof(VirtConsole),
149     .class_init    = virtconsole_class_init,
150 };
151
152 static Property virtserialport_properties[] = {
153     DEFINE_PROP_CHR("chardev", VirtConsole, chr),
154     DEFINE_PROP_END_OF_LIST(),
155 };
156
157 static void virtserialport_class_init(ObjectClass *klass, void *data)
158 {
159     DeviceClass *dc = DEVICE_CLASS(klass);
160     VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_CLASS(klass);
161
162     k->init = virtconsole_initfn;
163     k->have_data = flush_buf;
164     k->guest_open = guest_open;
165     k->guest_close = guest_close;
166     dc->props = virtserialport_properties;
167 }
168
169 static TypeInfo virtserialport_info = {
170     .name          = "virtserialport",
171     .parent        = TYPE_VIRTIO_SERIAL_PORT,
172     .instance_size = sizeof(VirtConsole),
173     .class_init    = virtserialport_class_init,
174 };
175
176 static void virtconsole_register_types(void)
177 {
178     type_register_static(&virtconsole_info);
179     type_register_static(&virtserialport_info);
180 }
181
182 type_init(virtconsole_register_types)