packing update
[qemu] / hw / virtio-console.c
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                                          PCI_CLASS_DISPLAY_OTHER, 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 }