Statistics
| Branch: | Revision:

root / hw / char / virtio-console.c @ dccfcd0e

History | View | Annotate | Download (5 kB)

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
} VirtConsole;
22

    
23
/*
24
 * Callback function that's called from chardevs when backend becomes
25
 * writable.
26
 */
27
static gboolean chr_write_unblocked(GIOChannel *chan, GIOCondition cond,
28
                                    void *opaque)
29
{
30
    VirtConsole *vcon = opaque;
31

    
32
    virtio_serial_throttle_port(&vcon->port, false);
33
    return FALSE;
34
}
35

    
36
/* Callback function that's called when the guest sends us data */
37
static ssize_t flush_buf(VirtIOSerialPort *port, const uint8_t *buf, size_t len)
38
{
39
    VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port);
40
    ssize_t ret;
41

    
42
    if (!vcon->chr) {
43
        /* If there's no backend, we can just say we consumed all data. */
44
        return len;
45
    }
46

    
47
    ret = qemu_chr_fe_write(vcon->chr, buf, len);
48
    trace_virtio_console_flush_buf(port->id, len, ret);
49

    
50
    if (ret <= 0) {
51
        VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(port);
52

    
53
        /*
54
         * Ideally we'd get a better error code than just -1, but
55
         * that's what the chardev interface gives us right now.  If
56
         * we had a finer-grained message, like -EPIPE, we could close
57
         * this connection.
58
         */
59
        ret = 0;
60
        if (!k->is_console) {
61
            virtio_serial_throttle_port(port, true);
62
            qemu_chr_fe_add_watch(vcon->chr, G_IO_OUT, chr_write_unblocked,
63
                                  vcon);
64
        }
65
    }
66
    return ret;
67
}
68

    
69
/* Callback function that's called when the guest opens/closes the port */
70
static void set_guest_connected(VirtIOSerialPort *port, int guest_connected)
71
{
72
    VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port);
73

    
74
    if (!vcon->chr) {
75
        return;
76
    }
77
    qemu_chr_fe_set_open(vcon->chr, guest_connected);
78
}
79

    
80
/* Readiness of the guest to accept data on a port */
81
static int chr_can_read(void *opaque)
82
{
83
    VirtConsole *vcon = opaque;
84

    
85
    return virtio_serial_guest_ready(&vcon->port);
86
}
87

    
88
/* Send data from a char device over to the guest */
89
static void chr_read(void *opaque, const uint8_t *buf, int size)
90
{
91
    VirtConsole *vcon = opaque;
92

    
93
    trace_virtio_console_chr_read(vcon->port.id, size);
94
    virtio_serial_write(&vcon->port, buf, size);
95
}
96

    
97
static void chr_event(void *opaque, int event)
98
{
99
    VirtConsole *vcon = opaque;
100

    
101
    trace_virtio_console_chr_event(vcon->port.id, event);
102
    switch (event) {
103
    case CHR_EVENT_OPENED:
104
        virtio_serial_open(&vcon->port);
105
        break;
106
    case CHR_EVENT_CLOSED:
107
        virtio_serial_close(&vcon->port);
108
        break;
109
    }
110
}
111

    
112
static int virtconsole_initfn(VirtIOSerialPort *port)
113
{
114
    VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port);
115
    VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(port);
116

    
117
    if (port->id == 0 && !k->is_console) {
118
        error_report("Port number 0 on virtio-serial devices reserved for virtconsole devices for backward compatibility.");
119
        return -1;
120
    }
121

    
122
    if (vcon->chr) {
123
        vcon->chr->explicit_fe_open = 1;
124
        qemu_chr_add_handlers(vcon->chr, chr_can_read, chr_read, chr_event,
125
                              vcon);
126
    }
127

    
128
    return 0;
129
}
130

    
131
static Property virtconsole_properties[] = {
132
    DEFINE_PROP_CHR("chardev", VirtConsole, chr),
133
    DEFINE_PROP_END_OF_LIST(),
134
};
135

    
136
static void virtconsole_class_init(ObjectClass *klass, void *data)
137
{
138
    DeviceClass *dc = DEVICE_CLASS(klass);
139
    VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_CLASS(klass);
140

    
141
    k->is_console = true;
142
    k->init = virtconsole_initfn;
143
    k->have_data = flush_buf;
144
    k->set_guest_connected = set_guest_connected;
145
    dc->props = virtconsole_properties;
146
}
147

    
148
static const TypeInfo virtconsole_info = {
149
    .name          = "virtconsole",
150
    .parent        = TYPE_VIRTIO_SERIAL_PORT,
151
    .instance_size = sizeof(VirtConsole),
152
    .class_init    = virtconsole_class_init,
153
};
154

    
155
static Property virtserialport_properties[] = {
156
    DEFINE_PROP_CHR("chardev", VirtConsole, chr),
157
    DEFINE_PROP_END_OF_LIST(),
158
};
159

    
160
static void virtserialport_class_init(ObjectClass *klass, void *data)
161
{
162
    DeviceClass *dc = DEVICE_CLASS(klass);
163
    VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_CLASS(klass);
164

    
165
    k->init = virtconsole_initfn;
166
    k->have_data = flush_buf;
167
    k->set_guest_connected = set_guest_connected;
168
    dc->props = virtserialport_properties;
169
}
170

    
171
static const TypeInfo virtserialport_info = {
172
    .name          = "virtserialport",
173
    .parent        = TYPE_VIRTIO_SERIAL_PORT,
174
    .instance_size = sizeof(VirtConsole),
175
    .class_init    = virtserialport_class_init,
176
};
177

    
178
static void virtconsole_register_types(void)
179
{
180
    type_register_static(&virtconsole_info);
181
    type_register_static(&virtserialport_info);
182
}
183

    
184
type_init(virtconsole_register_types)