root / hw / virtio-console.c @ 99b3718e
History | View | Annotate | Download (4 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, elem.out_sg[d].iov_base,elem.out_sg[d].iov_len); |
43 |
virtqueue_push(vq, &elem, len); |
44 |
virtio_notify(vdev, vq); |
45 |
} |
46 |
} |
47 |
|
48 |
static void virtio_console_handle_input(VirtIODevice *vdev, VirtQueue *vq) |
49 |
{ |
50 |
} |
51 |
|
52 |
static uint32_t virtio_console_get_features(VirtIODevice *vdev)
|
53 |
{ |
54 |
return 0; |
55 |
} |
56 |
|
57 |
static int vcon_can_read(void *opaque) |
58 |
{ |
59 |
VirtIOConsole *s = (VirtIOConsole *) opaque; |
60 |
|
61 |
if (!virtio_queue_ready(s->ivq) ||
|
62 |
!(s->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK) || |
63 |
virtio_queue_empty(s->ivq)) |
64 |
return 0; |
65 |
|
66 |
/* current implementations have a page sized buffer.
|
67 |
* We fall back to a one byte per read if there is not enough room.
|
68 |
* It would be cool to have a function that returns the available byte
|
69 |
* instead of checking for a limit */
|
70 |
if (virtqueue_avail_bytes(s->ivq, TARGET_PAGE_SIZE, 0)) |
71 |
return TARGET_PAGE_SIZE;
|
72 |
if (virtqueue_avail_bytes(s->ivq, 1, 0)) |
73 |
return 1; |
74 |
return 0; |
75 |
} |
76 |
|
77 |
static void vcon_read(void *opaque, const uint8_t *buf, int size) |
78 |
{ |
79 |
VirtIOConsole *s = (VirtIOConsole *) opaque; |
80 |
VirtQueueElement elem; |
81 |
int offset = 0; |
82 |
|
83 |
/* The current kernel implementation has only one outstanding input
|
84 |
* buffer of PAGE_SIZE. Nevertheless, this function is prepared to
|
85 |
* handle multiple buffers with multiple sg element for input */
|
86 |
while (offset < size) {
|
87 |
int i = 0; |
88 |
if (!virtqueue_pop(s->ivq, &elem))
|
89 |
break;
|
90 |
while (offset < size && i < elem.in_num) {
|
91 |
int len = MIN(elem.in_sg[i].iov_len, size - offset);
|
92 |
memcpy(elem.in_sg[i].iov_base, buf + offset, len); |
93 |
offset += len; |
94 |
i++; |
95 |
} |
96 |
virtqueue_push(s->ivq, &elem, size); |
97 |
} |
98 |
virtio_notify(&s->vdev, s->ivq); |
99 |
} |
100 |
|
101 |
static void vcon_event(void *opaque, int event) |
102 |
{ |
103 |
/* we will ignore any event for the time being */
|
104 |
} |
105 |
|
106 |
static void virtio_console_save(QEMUFile *f, void *opaque) |
107 |
{ |
108 |
VirtIOConsole *s = opaque; |
109 |
|
110 |
virtio_save(&s->vdev, f); |
111 |
} |
112 |
|
113 |
static int virtio_console_load(QEMUFile *f, void *opaque, int version_id) |
114 |
{ |
115 |
VirtIOConsole *s = opaque; |
116 |
|
117 |
if (version_id != 1) |
118 |
return -EINVAL;
|
119 |
|
120 |
virtio_load(&s->vdev, f); |
121 |
return 0; |
122 |
} |
123 |
|
124 |
void *virtio_console_init(PCIBus *bus, CharDriverState *chr)
|
125 |
{ |
126 |
VirtIOConsole *s; |
127 |
|
128 |
s = (VirtIOConsole *)virtio_init_pci(bus, "virtio-console",
|
129 |
PCI_VENDOR_ID_REDHAT_QUMRANET, |
130 |
PCI_DEVICE_ID_VIRTIO_CONSOLE, |
131 |
PCI_VENDOR_ID_REDHAT_QUMRANET, |
132 |
VIRTIO_ID_CONSOLE, |
133 |
0x03, 0x80, 0x00, |
134 |
0, sizeof(VirtIOConsole)); |
135 |
if (s == NULL) |
136 |
return NULL; |
137 |
|
138 |
s->vdev.get_features = virtio_console_get_features; |
139 |
|
140 |
s->ivq = virtio_add_queue(&s->vdev, 128, virtio_console_handle_input);
|
141 |
s->dvq = virtio_add_queue(&s->vdev, 128, virtio_console_handle_output);
|
142 |
|
143 |
s->chr = chr; |
144 |
qemu_chr_add_handlers(chr, vcon_can_read, vcon_read, vcon_event, s); |
145 |
|
146 |
register_savevm("virtio-console", -1, 1, virtio_console_save, virtio_console_load, s); |
147 |
|
148 |
return &s->vdev;
|
149 |
} |