Statistics
| Branch: | Revision:

root / spice-qemu-char.c @ 7a5448ce

History | View | Annotate | Download (8.1 kB)

1
#include "config-host.h"
2
#include "trace.h"
3
#include "ui/qemu-spice.h"
4
#include <spice.h>
5
#include <spice-experimental.h>
6
#include <spice/protocol.h>
7

    
8
#include "osdep.h"
9

    
10
#define dprintf(_scd, _level, _fmt, ...)                                \
11
    do {                                                                \
12
        static unsigned __dprintf_counter = 0;                          \
13
        if (_scd->debug >= _level) {                                    \
14
            fprintf(stderr, "scd: %3d: " _fmt, ++__dprintf_counter, ## __VA_ARGS__);\
15
        }                                                               \
16
    } while (0)
17

    
18
typedef struct SpiceCharDriver {
19
    CharDriverState*      chr;
20
    SpiceCharDeviceInstance     sin;
21
    char                  *subtype;
22
    bool                  active;
23
    uint8_t               *buffer;
24
    uint8_t               *datapos;
25
    ssize_t               bufsize, datalen;
26
    uint32_t              debug;
27
    QLIST_ENTRY(SpiceCharDriver) next;
28
} SpiceCharDriver;
29

    
30
static QLIST_HEAD(, SpiceCharDriver) spice_chars =
31
    QLIST_HEAD_INITIALIZER(spice_chars);
32

    
33
static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
34
{
35
    SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
36
    ssize_t out = 0;
37
    ssize_t last_out;
38
    uint8_t* p = (uint8_t*)buf;
39

    
40
    while (len > 0) {
41
        last_out = MIN(len, qemu_chr_be_can_write(scd->chr));
42
        if (last_out <= 0) {
43
            break;
44
        }
45
        qemu_chr_be_write(scd->chr, p, last_out);
46
        out += last_out;
47
        len -= last_out;
48
        p += last_out;
49
    }
50

    
51
    dprintf(scd, 3, "%s: %zu/%zd\n", __func__, out, len + out);
52
    trace_spice_vmc_write(out, len + out);
53
    return out;
54
}
55

    
56
static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len)
57
{
58
    SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
59
    int bytes = MIN(len, scd->datalen);
60

    
61
    dprintf(scd, 2, "%s: %p %d/%d/%zd\n", __func__, scd->datapos, len, bytes, scd->datalen);
62
    if (bytes > 0) {
63
        memcpy(buf, scd->datapos, bytes);
64
        scd->datapos += bytes;
65
        scd->datalen -= bytes;
66
        assert(scd->datalen >= 0);
67
        if (scd->datalen == 0) {
68
            scd->datapos = 0;
69
        }
70
    }
71
    trace_spice_vmc_read(bytes, len);
72
    return bytes;
73
}
74

    
75
#if SPICE_SERVER_VERSION >= 0x000c02
76
static void vmc_event(SpiceCharDeviceInstance *sin, uint8_t event)
77
{
78
    SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
79
    int chr_event;
80

    
81
    switch (event) {
82
    case SPICE_PORT_EVENT_BREAK:
83
        chr_event = CHR_EVENT_BREAK;
84
        break;
85
    default:
86
        dprintf(scd, 2, "%s: unknown %d\n", __func__, event);
87
        return;
88
    }
89

    
90
    dprintf(scd, 2, "%s: %d\n", __func__, event);
91
    trace_spice_vmc_event(chr_event);
92
    qemu_chr_be_event(scd->chr, chr_event);
93
}
94
#endif
95

    
96
static void vmc_state(SpiceCharDeviceInstance *sin, int connected)
97
{
98
    SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
99

    
100
#if SPICE_SERVER_VERSION < 0x000901
101
    /*
102
     * spice-server calls the state callback for the agent channel when the
103
     * spice client connects / disconnects. Given that not the client but
104
     * the server is doing the parsing of the messages this is wrong as the
105
     * server is still listening. Worse, this causes the parser in the server
106
     * to go out of sync, so we ignore state calls for subtype vdagent
107
     * spicevmc chardevs. For the full story see:
108
     * http://lists.freedesktop.org/archives/spice-devel/2011-July/004837.html
109
     */
110
    if (strcmp(sin->subtype, "vdagent") == 0) {
111
        return;
112
    }
113
#endif
114

    
115
    if ((scd->chr->opened && connected) ||
116
        (!scd->chr->opened && !connected)) {
117
        return;
118
    }
119

    
120
    qemu_chr_be_event(scd->chr,
121
                      connected ? CHR_EVENT_OPENED : CHR_EVENT_CLOSED);
122
}
123

    
124
static SpiceCharDeviceInterface vmc_interface = {
125
    .base.type          = SPICE_INTERFACE_CHAR_DEVICE,
126
    .base.description   = "spice virtual channel char device",
127
    .base.major_version = SPICE_INTERFACE_CHAR_DEVICE_MAJOR,
128
    .base.minor_version = SPICE_INTERFACE_CHAR_DEVICE_MINOR,
129
    .state              = vmc_state,
130
    .write              = vmc_write,
131
    .read               = vmc_read,
132
#if SPICE_SERVER_VERSION >= 0x000c02
133
    .event              = vmc_event,
134
#endif
135
};
136

    
137

    
138
static void vmc_register_interface(SpiceCharDriver *scd)
139
{
140
    if (scd->active) {
141
        return;
142
    }
143
    dprintf(scd, 1, "%s\n", __func__);
144
    scd->sin.base.sif = &vmc_interface.base;
145
    qemu_spice_add_interface(&scd->sin.base);
146
    scd->active = true;
147
    trace_spice_vmc_register_interface(scd);
148
}
149

    
150
static void vmc_unregister_interface(SpiceCharDriver *scd)
151
{
152
    if (!scd->active) {
153
        return;
154
    }
155
    dprintf(scd, 1, "%s\n", __func__);
156
    spice_server_remove_interface(&scd->sin.base);
157
    scd->active = false;
158
    trace_spice_vmc_unregister_interface(scd);
159
}
160

    
161

    
162
static int spice_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
163
{
164
    SpiceCharDriver *s = chr->opaque;
165

    
166
    dprintf(s, 2, "%s: %d\n", __func__, len);
167
    vmc_register_interface(s);
168
    assert(s->datalen == 0);
169
    if (s->bufsize < len) {
170
        s->bufsize = len;
171
        s->buffer = g_realloc(s->buffer, s->bufsize);
172
    }
173
    memcpy(s->buffer, buf, len);
174
    s->datapos = s->buffer;
175
    s->datalen = len;
176
    spice_server_char_device_wakeup(&s->sin);
177
    return len;
178
}
179

    
180
static void spice_chr_close(struct CharDriverState *chr)
181
{
182
    SpiceCharDriver *s = chr->opaque;
183

    
184
    printf("%s\n", __func__);
185
    vmc_unregister_interface(s);
186
    QLIST_REMOVE(s, next);
187
    g_free(s);
188
}
189

    
190
static void spice_chr_guest_open(struct CharDriverState *chr)
191
{
192
    SpiceCharDriver *s = chr->opaque;
193
    vmc_register_interface(s);
194
}
195

    
196
static void spice_chr_guest_close(struct CharDriverState *chr)
197
{
198
    SpiceCharDriver *s = chr->opaque;
199
    vmc_unregister_interface(s);
200
}
201

    
202
static void print_allowed_subtypes(void)
203
{
204
    const char** psubtype;
205
    int i;
206

    
207
    fprintf(stderr, "allowed names: ");
208
    for(i=0, psubtype = spice_server_char_device_recognized_subtypes();
209
        *psubtype != NULL; ++psubtype, ++i) {
210
        if (i == 0) {
211
            fprintf(stderr, "%s", *psubtype);
212
        } else {
213
            fprintf(stderr, ", %s", *psubtype);
214
        }
215
    }
216
    fprintf(stderr, "\n");
217
}
218

    
219
static CharDriverState *chr_open(QemuOpts *opts, const char *subtype)
220
{
221
    CharDriverState *chr;
222
    SpiceCharDriver *s;
223
    uint32_t debug = qemu_opt_get_number(opts, "debug", 0);
224

    
225
    chr = g_malloc0(sizeof(CharDriverState));
226
    s = g_malloc0(sizeof(SpiceCharDriver));
227
    s->chr = chr;
228
    s->debug = debug;
229
    s->active = false;
230
    s->sin.subtype = subtype;
231
    chr->opaque = s;
232
    chr->chr_write = spice_chr_write;
233
    chr->chr_close = spice_chr_close;
234
    chr->chr_guest_open = spice_chr_guest_open;
235
    chr->chr_guest_close = spice_chr_guest_close;
236

    
237
    QLIST_INSERT_HEAD(&spice_chars, s, next);
238

    
239
    return chr;
240
}
241

    
242
CharDriverState *qemu_chr_open_spice(QemuOpts *opts)
243
{
244
    CharDriverState *chr;
245
    const char *name = qemu_opt_get(opts, "name");
246
    const char **psubtype = spice_server_char_device_recognized_subtypes();
247
    const char *subtype = NULL;
248

    
249
    if (name == NULL) {
250
        fprintf(stderr, "spice-qemu-char: missing name parameter\n");
251
        print_allowed_subtypes();
252
        return NULL;
253
    }
254
    for(;*psubtype != NULL; ++psubtype) {
255
        if (strcmp(name, *psubtype) == 0) {
256
            subtype = *psubtype;
257
            break;
258
        }
259
    }
260
    if (subtype == NULL) {
261
        fprintf(stderr, "spice-qemu-char: unsupported name: %s\n", name);
262
        print_allowed_subtypes();
263
        return NULL;
264
    }
265

    
266
    chr = chr_open(opts, subtype);
267

    
268
#if SPICE_SERVER_VERSION < 0x000901
269
    /* See comment in vmc_state() */
270
    if (strcmp(subtype, "vdagent") == 0) {
271
        qemu_chr_generic_open(chr);
272
    }
273
#endif
274

    
275
    return chr;
276
}
277

    
278
#if SPICE_SERVER_VERSION >= 0x000c02
279
CharDriverState *qemu_chr_open_spice_port(QemuOpts *opts)
280
{
281
    CharDriverState *chr;
282
    SpiceCharDriver *s;
283
    const char *name = qemu_opt_get(opts, "name");
284

    
285
    if (name == NULL) {
286
        fprintf(stderr, "spice-qemu-char: missing name parameter\n");
287
        return NULL;
288
    }
289

    
290
    chr = chr_open(opts, "port");
291
    s = chr->opaque;
292
    s->sin.portname = name;
293

    
294
    return chr;
295
}
296
#endif