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 "sysemu/char.h" 14 #include "qemu/error-report.h" 15 #include "trace.h" 16 #include "hw/virtio/virtio-serial.h" 17 #include "qapi-event.h" 18 19 #define TYPE_VIRTIO_CONSOLE_SERIAL_PORT "virtserialport" 20 #define VIRTIO_CONSOLE(obj) \ 21 OBJECT_CHECK(VirtConsole, (obj), TYPE_VIRTIO_CONSOLE_SERIAL_PORT) 22 23 typedef struct VirtConsole { 24 VirtIOSerialPort parent_obj; 25 26 CharDriverState *chr; 27 guint watch; 28 } VirtConsole; 29 30 /* 31 * Callback function that's called from chardevs when backend becomes 32 * writable. 33 */ 34 static gboolean chr_write_unblocked(GIOChannel *chan, GIOCondition cond, 35 void *opaque) 36 { 37 VirtConsole *vcon = opaque; 38 39 vcon->watch = 0; 40 virtio_serial_throttle_port(VIRTIO_SERIAL_PORT(vcon), false); 41 return FALSE; 42 } 43 44 /* Callback function that's called when the guest sends us data */ 45 static ssize_t flush_buf(VirtIOSerialPort *port, 46 const uint8_t *buf, ssize_t len) 47 { 48 VirtConsole *vcon = VIRTIO_CONSOLE(port); 49 ssize_t ret; 50 51 if (!vcon->chr) { 52 /* If there's no backend, we can just say we consumed all data. */ 53 return len; 54 } 55 56 ret = qemu_chr_fe_write(vcon->chr, buf, len); 57 trace_virtio_console_flush_buf(port->id, len, ret); 58 59 if (ret < len) { 60 VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(port); 61 62 /* 63 * Ideally we'd get a better error code than just -1, but 64 * that's what the chardev interface gives us right now. If 65 * we had a finer-grained message, like -EPIPE, we could close 66 * this connection. 67 */ 68 if (ret < 0) 69 ret = 0; 70 if (!k->is_console) { 71 virtio_serial_throttle_port(port, true); 72 if (!vcon->watch) { 73 vcon->watch = qemu_chr_fe_add_watch(vcon->chr, G_IO_OUT, 74 chr_write_unblocked, vcon); 75 } 76 } 77 } 78 return ret; 79 } 80 81 /* Callback function that's called when the guest opens/closes the port */ 82 static void set_guest_connected(VirtIOSerialPort *port, int guest_connected) 83 { 84 VirtConsole *vcon = VIRTIO_CONSOLE(port); 85 DeviceState *dev = DEVICE(port); 86 87 if (vcon->chr) { 88 qemu_chr_fe_set_open(vcon->chr, guest_connected); 89 } 90 91 if (dev->id) { 92 qapi_event_send_vserport_change(dev->id, guest_connected, 93 &error_abort); 94 } 95 } 96 97 /* Readiness of the guest to accept data on a port */ 98 static int chr_can_read(void *opaque) 99 { 100 VirtConsole *vcon = opaque; 101 102 return virtio_serial_guest_ready(VIRTIO_SERIAL_PORT(vcon)); 103 } 104 105 /* Send data from a char device over to the guest */ 106 static void chr_read(void *opaque, const uint8_t *buf, int size) 107 { 108 VirtConsole *vcon = opaque; 109 VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(vcon); 110 111 trace_virtio_console_chr_read(port->id, size); 112 virtio_serial_write(port, buf, size); 113 } 114 115 static void chr_event(void *opaque, int event) 116 { 117 VirtConsole *vcon = opaque; 118 VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(vcon); 119 120 trace_virtio_console_chr_event(port->id, event); 121 switch (event) { 122 case CHR_EVENT_OPENED: 123 virtio_serial_open(port); 124 break; 125 case CHR_EVENT_CLOSED: 126 if (vcon->watch) { 127 g_source_remove(vcon->watch); 128 vcon->watch = 0; 129 } 130 virtio_serial_close(port); 131 break; 132 } 133 } 134 135 static void virtconsole_realize(DeviceState *dev, Error **errp) 136 { 137 VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(dev); 138 VirtConsole *vcon = VIRTIO_CONSOLE(dev); 139 VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(dev); 140 141 if (port->id == 0 && !k->is_console) { 142 error_setg(errp, "Port number 0 on virtio-serial devices reserved " 143 "for virtconsole devices for backward compatibility."); 144 return; 145 } 146 147 if (vcon->chr) { 148 vcon->chr->explicit_fe_open = 1; 149 qemu_chr_add_handlers(vcon->chr, chr_can_read, chr_read, chr_event, 150 vcon); 151 } 152 } 153 154 static void virtconsole_unrealize(DeviceState *dev, Error **errp) 155 { 156 VirtConsole *vcon = VIRTIO_CONSOLE(dev); 157 158 if (vcon->watch) { 159 g_source_remove(vcon->watch); 160 } 161 } 162 163 static void virtconsole_class_init(ObjectClass *klass, void *data) 164 { 165 VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_CLASS(klass); 166 167 k->is_console = true; 168 } 169 170 static const TypeInfo virtconsole_info = { 171 .name = "virtconsole", 172 .parent = TYPE_VIRTIO_CONSOLE_SERIAL_PORT, 173 .class_init = virtconsole_class_init, 174 }; 175 176 static Property virtserialport_properties[] = { 177 DEFINE_PROP_CHR("chardev", VirtConsole, chr), 178 DEFINE_PROP_END_OF_LIST(), 179 }; 180 181 static void virtserialport_class_init(ObjectClass *klass, void *data) 182 { 183 DeviceClass *dc = DEVICE_CLASS(klass); 184 VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_CLASS(klass); 185 186 k->realize = virtconsole_realize; 187 k->unrealize = virtconsole_unrealize; 188 k->have_data = flush_buf; 189 k->set_guest_connected = set_guest_connected; 190 dc->props = virtserialport_properties; 191 } 192 193 static const TypeInfo virtserialport_info = { 194 .name = TYPE_VIRTIO_CONSOLE_SERIAL_PORT, 195 .parent = TYPE_VIRTIO_SERIAL_PORT, 196 .instance_size = sizeof(VirtConsole), 197 .class_init = virtserialport_class_init, 198 }; 199 200 static void virtconsole_register_types(void) 201 { 202 type_register_static(&virtserialport_info); 203 type_register_static(&virtconsole_info); 204 } 205 206 type_init(virtconsole_register_types) 207