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