Statistics
| Branch: | Revision:

root / hw / virtio-console.c @ cf21e106

History | View | Annotate | Download (4.2 kB)

1
/*
2
 * Virtio Console Device
3
 *
4
 * Copyright IBM, Corp. 2008
5
 *
6
 * Authors:
7
 *  Christian Ehrhardt <ehrhardt@linux.vnet.ibm.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

    
14
#include "hw.h"
15
#include "qemu-char.h"
16
#include "virtio.h"
17
#include "virtio-console.h"
18

    
19

    
20
typedef struct VirtIOConsole
21
{
22
    VirtIODevice vdev;
23
    VirtQueue *ivq, *dvq;
24
    CharDriverState *chr;
25
} VirtIOConsole;
26

    
27
static VirtIOConsole *to_virtio_console(VirtIODevice *vdev)
28
{
29
    return (VirtIOConsole *)vdev;
30
}
31

    
32
static void virtio_console_handle_output(VirtIODevice *vdev, VirtQueue *vq)
33
{
34
    VirtIOConsole *s = to_virtio_console(vdev);
35
    VirtQueueElement elem;
36

    
37
    while (virtqueue_pop(vq, &elem)) {
38
        ssize_t len = 0;
39
        int d;
40

    
41
        for (d = 0; d < elem.out_num; d++) {
42
            len += qemu_chr_write(s->chr, (uint8_t *)elem.out_sg[d].iov_base,
43
                                  elem.out_sg[d].iov_len);
44
        }
45
        virtqueue_push(vq, &elem, len);
46
        virtio_notify(vdev, vq);
47
    }
48
}
49

    
50
static void virtio_console_handle_input(VirtIODevice *vdev, VirtQueue *vq)
51
{
52
}
53

    
54
static uint32_t virtio_console_get_features(VirtIODevice *vdev)
55
{
56
    return 0;
57
}
58

    
59
static int vcon_can_read(void *opaque)
60
{
61
    VirtIOConsole *s = (VirtIOConsole *) opaque;
62

    
63
    if (!virtio_queue_ready(s->ivq) ||
64
        !(s->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK) ||
65
        virtio_queue_empty(s->ivq))
66
        return 0;
67

    
68
    /* current implementations have a page sized buffer.
69
     * We fall back to a one byte per read if there is not enough room.
70
     * It would be cool to have a function that returns the available byte
71
     * instead of checking for a limit */
72
    if (virtqueue_avail_bytes(s->ivq, TARGET_PAGE_SIZE, 0))
73
        return TARGET_PAGE_SIZE;
74
    if (virtqueue_avail_bytes(s->ivq, 1, 0))
75
        return 1;
76
    return 0;
77
}
78

    
79
static void vcon_read(void *opaque, const uint8_t *buf, int size)
80
{
81
    VirtIOConsole *s = (VirtIOConsole *) opaque;
82
    VirtQueueElement elem;
83
    int offset = 0;
84

    
85
    /* The current kernel implementation has only one outstanding input
86
     * buffer of PAGE_SIZE. Nevertheless, this function is prepared to
87
     * handle multiple buffers with multiple sg element for input */
88
    while (offset < size) {
89
        int i = 0;
90
        if (!virtqueue_pop(s->ivq, &elem))
91
                break;
92
        while (offset < size && i < elem.in_num) {
93
            int len = MIN(elem.in_sg[i].iov_len, size - offset);
94
            memcpy(elem.in_sg[i].iov_base, buf + offset, len);
95
            offset += len;
96
            i++;
97
        }
98
        virtqueue_push(s->ivq, &elem, size);
99
    }
100
    virtio_notify(&s->vdev, s->ivq);
101
}
102

    
103
static void vcon_event(void *opaque, int event)
104
{
105
    /* we will ignore any event for the time being */
106
}
107

    
108
static void virtio_console_save(QEMUFile *f, void *opaque)
109
{
110
    VirtIOConsole *s = opaque;
111

    
112
    virtio_save(&s->vdev, f);
113
}
114

    
115
static int virtio_console_load(QEMUFile *f, void *opaque, int version_id)
116
{
117
    VirtIOConsole *s = opaque;
118

    
119
    if (version_id != 1)
120
        return -EINVAL;
121

    
122
    virtio_load(&s->vdev, f);
123
    return 0;
124
}
125

    
126
void *virtio_console_init(PCIBus *bus, CharDriverState *chr)
127
{
128
    VirtIOConsole *s;
129
    PCIDevice *d;
130

    
131
    d = pci_register_device(bus, "virtio-console", sizeof(VirtIOConsole),
132
                            -1, NULL, NULL);
133
    if (!d)
134
        return NULL;
135

    
136
    s = (VirtIOConsole *)virtio_init_pci(d, "virtio-console",
137
                                         PCI_VENDOR_ID_REDHAT_QUMRANET,
138
                                         PCI_DEVICE_ID_VIRTIO_CONSOLE,
139
                                         PCI_VENDOR_ID_REDHAT_QUMRANET,
140
                                         VIRTIO_ID_CONSOLE,
141
                                         PCI_CLASS_DISPLAY_OTHER, 0x00,
142
                                         0);
143
    if (s == NULL)
144
        return NULL;
145

    
146
    s->vdev.get_features = virtio_console_get_features;
147

    
148
    s->ivq = virtio_add_queue(&s->vdev, 128, virtio_console_handle_input);
149
    s->dvq = virtio_add_queue(&s->vdev, 128, virtio_console_handle_output);
150

    
151
    s->chr = chr;
152
    qemu_chr_add_handlers(chr, vcon_can_read, vcon_read, vcon_event, s);
153

    
154
    register_savevm("virtio-console", -1, 1, virtio_console_save, virtio_console_load, s);
155

    
156
    return &s->vdev;
157
}