root / hw / virtio-console.c @ a9ff9df1
History | View | Annotate | Download (3.8 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 |
VirtIODevice *virtio_console_init(DeviceState *dev) |
127 |
{ |
128 |
VirtIOConsole *s; |
129 |
s = (VirtIOConsole *)virtio_common_init("virtio-console",
|
130 |
VIRTIO_ID_CONSOLE, |
131 |
0, sizeof(VirtIOConsole)); |
132 |
s->vdev.get_features = virtio_console_get_features; |
133 |
|
134 |
s->ivq = virtio_add_queue(&s->vdev, 128, virtio_console_handle_input);
|
135 |
s->dvq = virtio_add_queue(&s->vdev, 128, virtio_console_handle_output);
|
136 |
|
137 |
s->chr = qdev_init_chardev(dev); |
138 |
qemu_chr_add_handlers(s->chr, vcon_can_read, vcon_read, vcon_event, s); |
139 |
|
140 |
register_savevm("virtio-console", -1, 1, virtio_console_save, virtio_console_load, s); |
141 |
|
142 |
return &s->vdev;
|
143 |
} |