root / hw / virtio-console.c @ 3c4cf535
History | View | Annotate | Download (4 kB)
1 | 970d878c | aliguori | /*
|
---|---|---|---|
2 | 970d878c | aliguori | * Virtio Console Device
|
3 | 970d878c | aliguori | *
|
4 | 970d878c | aliguori | * Copyright IBM, Corp. 2008
|
5 | 970d878c | aliguori | *
|
6 | 970d878c | aliguori | * Authors:
|
7 | 970d878c | aliguori | * Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
|
8 | 970d878c | aliguori | *
|
9 | 970d878c | aliguori | * This work is licensed under the terms of the GNU GPL, version 2. See
|
10 | 970d878c | aliguori | * the COPYING file in the top-level directory.
|
11 | 970d878c | aliguori | *
|
12 | 970d878c | aliguori | */
|
13 | 970d878c | aliguori | |
14 | 970d878c | aliguori | #include "hw.h" |
15 | 970d878c | aliguori | #include "qemu-char.h" |
16 | 970d878c | aliguori | #include "virtio.h" |
17 | 970d878c | aliguori | #include "virtio-console.h" |
18 | 970d878c | aliguori | |
19 | 970d878c | aliguori | |
20 | 970d878c | aliguori | typedef struct VirtIOConsole |
21 | 970d878c | aliguori | { |
22 | 970d878c | aliguori | VirtIODevice vdev; |
23 | 970d878c | aliguori | VirtQueue *ivq, *dvq; |
24 | 970d878c | aliguori | CharDriverState *chr; |
25 | 970d878c | aliguori | } VirtIOConsole; |
26 | 970d878c | aliguori | |
27 | 970d878c | aliguori | static VirtIOConsole *to_virtio_console(VirtIODevice *vdev)
|
28 | 970d878c | aliguori | { |
29 | 970d878c | aliguori | return (VirtIOConsole *)vdev;
|
30 | 970d878c | aliguori | } |
31 | 970d878c | aliguori | |
32 | 970d878c | aliguori | static void virtio_console_handle_output(VirtIODevice *vdev, VirtQueue *vq) |
33 | 970d878c | aliguori | { |
34 | 970d878c | aliguori | VirtIOConsole *s = to_virtio_console(vdev); |
35 | 970d878c | aliguori | VirtQueueElement elem; |
36 | 970d878c | aliguori | |
37 | 970d878c | aliguori | while (virtqueue_pop(vq, &elem)) {
|
38 | 970d878c | aliguori | ssize_t len = 0;
|
39 | 970d878c | aliguori | int d;
|
40 | 970d878c | aliguori | |
41 | 970d878c | aliguori | for (d=0; d < elem.out_num; d++) |
42 | 970d878c | aliguori | len += qemu_chr_write(s->chr, elem.out_sg[d].iov_base,elem.out_sg[d].iov_len); |
43 | 970d878c | aliguori | virtqueue_push(vq, &elem, len); |
44 | 970d878c | aliguori | virtio_notify(vdev, vq); |
45 | 970d878c | aliguori | } |
46 | 970d878c | aliguori | } |
47 | 970d878c | aliguori | |
48 | 970d878c | aliguori | static void virtio_console_handle_input(VirtIODevice *vdev, VirtQueue *vq) |
49 | 970d878c | aliguori | { |
50 | 970d878c | aliguori | } |
51 | 970d878c | aliguori | |
52 | 970d878c | aliguori | static uint32_t virtio_console_get_features(VirtIODevice *vdev)
|
53 | 970d878c | aliguori | { |
54 | 970d878c | aliguori | return 0; |
55 | 970d878c | aliguori | } |
56 | 970d878c | aliguori | |
57 | 970d878c | aliguori | static int vcon_can_read(void *opaque) |
58 | 970d878c | aliguori | { |
59 | 970d878c | aliguori | VirtIOConsole *s = (VirtIOConsole *) opaque; |
60 | 970d878c | aliguori | |
61 | 970d878c | aliguori | if (!virtio_queue_ready(s->ivq) ||
|
62 | 970d878c | aliguori | !(s->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK) || |
63 | 970d878c | aliguori | virtio_queue_empty(s->ivq)) |
64 | 970d878c | aliguori | return 0; |
65 | 970d878c | aliguori | |
66 | 970d878c | aliguori | /* current implementations have a page sized buffer.
|
67 | 970d878c | aliguori | * We fall back to a one byte per read if there is not enough room.
|
68 | 970d878c | aliguori | * It would be cool to have a function that returns the available byte
|
69 | 970d878c | aliguori | * instead of checking for a limit */
|
70 | 970d878c | aliguori | if (virtqueue_avail_bytes(s->ivq, TARGET_PAGE_SIZE, 0)) |
71 | 970d878c | aliguori | return TARGET_PAGE_SIZE;
|
72 | 970d878c | aliguori | if (virtqueue_avail_bytes(s->ivq, 1, 0)) |
73 | 970d878c | aliguori | return 1; |
74 | 970d878c | aliguori | return 0; |
75 | 970d878c | aliguori | } |
76 | 970d878c | aliguori | |
77 | 970d878c | aliguori | static void vcon_read(void *opaque, const uint8_t *buf, int size) |
78 | 970d878c | aliguori | { |
79 | 970d878c | aliguori | VirtIOConsole *s = (VirtIOConsole *) opaque; |
80 | 970d878c | aliguori | VirtQueueElement elem; |
81 | 970d878c | aliguori | int offset = 0; |
82 | 970d878c | aliguori | |
83 | 970d878c | aliguori | /* The current kernel implementation has only one outstanding input
|
84 | 970d878c | aliguori | * buffer of PAGE_SIZE. Nevertheless, this function is prepared to
|
85 | 970d878c | aliguori | * handle multiple buffers with multiple sg element for input */
|
86 | 970d878c | aliguori | while (offset < size) {
|
87 | 970d878c | aliguori | int i = 0; |
88 | 970d878c | aliguori | if (!virtqueue_pop(s->ivq, &elem))
|
89 | 970d878c | aliguori | break;
|
90 | 970d878c | aliguori | while (offset < size && i < elem.in_num) {
|
91 | 970d878c | aliguori | int len = MIN(elem.in_sg[i].iov_len, size - offset);
|
92 | 970d878c | aliguori | memcpy(elem.in_sg[i].iov_base, buf + offset, len); |
93 | 970d878c | aliguori | offset += len; |
94 | 970d878c | aliguori | i++; |
95 | 970d878c | aliguori | } |
96 | 970d878c | aliguori | virtqueue_push(s->ivq, &elem, size); |
97 | 970d878c | aliguori | } |
98 | 970d878c | aliguori | virtio_notify(&s->vdev, s->ivq); |
99 | 970d878c | aliguori | } |
100 | 970d878c | aliguori | |
101 | 970d878c | aliguori | static void vcon_event(void *opaque, int event) |
102 | 970d878c | aliguori | { |
103 | 970d878c | aliguori | /* we will ignore any event for the time being */
|
104 | 970d878c | aliguori | } |
105 | 970d878c | aliguori | |
106 | 970d878c | aliguori | static void virtio_console_save(QEMUFile *f, void *opaque) |
107 | 970d878c | aliguori | { |
108 | 970d878c | aliguori | VirtIOConsole *s = opaque; |
109 | 970d878c | aliguori | |
110 | 970d878c | aliguori | virtio_save(&s->vdev, f); |
111 | 970d878c | aliguori | } |
112 | 970d878c | aliguori | |
113 | 970d878c | aliguori | static int virtio_console_load(QEMUFile *f, void *opaque, int version_id) |
114 | 970d878c | aliguori | { |
115 | 970d878c | aliguori | VirtIOConsole *s = opaque; |
116 | 970d878c | aliguori | |
117 | 970d878c | aliguori | if (version_id != 1) |
118 | 970d878c | aliguori | return -EINVAL;
|
119 | 970d878c | aliguori | |
120 | 970d878c | aliguori | virtio_load(&s->vdev, f); |
121 | 970d878c | aliguori | return 0; |
122 | 970d878c | aliguori | } |
123 | 970d878c | aliguori | |
124 | 970d878c | aliguori | void *virtio_console_init(PCIBus *bus, CharDriverState *chr)
|
125 | 970d878c | aliguori | { |
126 | 970d878c | aliguori | VirtIOConsole *s; |
127 | 970d878c | aliguori | |
128 | 970d878c | aliguori | s = (VirtIOConsole *)virtio_init_pci(bus, "virtio-console",
|
129 | 14d50bef | aliguori | PCI_VENDOR_ID_REDHAT_QUMRANET, |
130 | 14d50bef | aliguori | PCI_DEVICE_ID_VIRTIO_CONSOLE, |
131 | 99b3718e | aliguori | PCI_VENDOR_ID_REDHAT_QUMRANET, |
132 | 99b3718e | aliguori | VIRTIO_ID_CONSOLE, |
133 | 173a543b | blueswir1 | PCI_CLASS_DISPLAY_OTHER, 0x00,
|
134 | 970d878c | aliguori | 0, sizeof(VirtIOConsole)); |
135 | 970d878c | aliguori | if (s == NULL) |
136 | 970d878c | aliguori | return NULL; |
137 | 970d878c | aliguori | |
138 | 970d878c | aliguori | s->vdev.get_features = virtio_console_get_features; |
139 | 970d878c | aliguori | |
140 | 970d878c | aliguori | s->ivq = virtio_add_queue(&s->vdev, 128, virtio_console_handle_input);
|
141 | 970d878c | aliguori | s->dvq = virtio_add_queue(&s->vdev, 128, virtio_console_handle_output);
|
142 | 970d878c | aliguori | |
143 | 970d878c | aliguori | s->chr = chr; |
144 | 970d878c | aliguori | qemu_chr_add_handlers(chr, vcon_can_read, vcon_read, vcon_event, s); |
145 | 970d878c | aliguori | |
146 | 970d878c | aliguori | register_savevm("virtio-console", -1, 1, virtio_console_save, virtio_console_load, s); |
147 | 970d878c | aliguori | |
148 | 970d878c | aliguori | return &s->vdev;
|
149 | 970d878c | aliguori | } |