123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149 |
- /*
- * Virtio Console Device
- *
- * Copyright IBM, Corp. 2008
- *
- * Authors:
- * Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2. See
- * the COPYING file in the top-level directory.
- *
- */
- #include "hw.h"
- #include "qemu-char.h"
- #include "virtio.h"
- #include "virtio-console.h"
- typedef struct VirtIOConsole
- {
- VirtIODevice vdev;
- VirtQueue *ivq, *dvq;
- CharDriverState *chr;
- } VirtIOConsole;
- static VirtIOConsole *to_virtio_console(VirtIODevice *vdev)
- {
- return (VirtIOConsole *)vdev;
- }
- static void virtio_console_handle_output(VirtIODevice *vdev, VirtQueue *vq)
- {
- VirtIOConsole *s = to_virtio_console(vdev);
- VirtQueueElement elem;
- while (virtqueue_pop(vq, &elem)) {
- ssize_t len = 0;
- int d;
- for (d=0; d < elem.out_num; d++)
- len += qemu_chr_write(s->chr, elem.out_sg[d].iov_base,elem.out_sg[d].iov_len);
- virtqueue_push(vq, &elem, len);
- virtio_notify(vdev, vq);
- }
- }
- static void virtio_console_handle_input(VirtIODevice *vdev, VirtQueue *vq)
- {
- }
- static uint32_t virtio_console_get_features(VirtIODevice *vdev)
- {
- return 0;
- }
- static int vcon_can_read(void *opaque)
- {
- VirtIOConsole *s = (VirtIOConsole *) opaque;
- if (!virtio_queue_ready(s->ivq) ||
- !(s->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK) ||
- virtio_queue_empty(s->ivq))
- return 0;
- /* current implementations have a page sized buffer.
- * We fall back to a one byte per read if there is not enough room.
- * It would be cool to have a function that returns the available byte
- * instead of checking for a limit */
- if (virtqueue_avail_bytes(s->ivq, TARGET_PAGE_SIZE, 0))
- return TARGET_PAGE_SIZE;
- if (virtqueue_avail_bytes(s->ivq, 1, 0))
- return 1;
- return 0;
- }
- static void vcon_read(void *opaque, const uint8_t *buf, int size)
- {
- VirtIOConsole *s = (VirtIOConsole *) opaque;
- VirtQueueElement elem;
- int offset = 0;
- /* The current kernel implementation has only one outstanding input
- * buffer of PAGE_SIZE. Nevertheless, this function is prepared to
- * handle multiple buffers with multiple sg element for input */
- while (offset < size) {
- int i = 0;
- if (!virtqueue_pop(s->ivq, &elem))
- break;
- while (offset < size && i < elem.in_num) {
- int len = MIN(elem.in_sg[i].iov_len, size - offset);
- memcpy(elem.in_sg[i].iov_base, buf + offset, len);
- offset += len;
- i++;
- }
- virtqueue_push(s->ivq, &elem, size);
- }
- virtio_notify(&s->vdev, s->ivq);
- }
- static void vcon_event(void *opaque, int event)
- {
- /* we will ignore any event for the time being */
- }
- static void virtio_console_save(QEMUFile *f, void *opaque)
- {
- VirtIOConsole *s = opaque;
- virtio_save(&s->vdev, f);
- }
- static int virtio_console_load(QEMUFile *f, void *opaque, int version_id)
- {
- VirtIOConsole *s = opaque;
- if (version_id != 1)
- return -EINVAL;
- virtio_load(&s->vdev, f);
- return 0;
- }
- void *virtio_console_init(PCIBus *bus, CharDriverState *chr)
- {
- VirtIOConsole *s;
- s = (VirtIOConsole *)virtio_init_pci(bus, "virtio-console",
- PCI_VENDOR_ID_REDHAT_QUMRANET,
- PCI_DEVICE_ID_VIRTIO_CONSOLE,
- PCI_VENDOR_ID_REDHAT_QUMRANET,
- VIRTIO_ID_CONSOLE,
- PCI_CLASS_DISPLAY_OTHER, 0x00,
- 0, sizeof(VirtIOConsole));
- if (s == NULL)
- return NULL;
- s->vdev.get_features = virtio_console_get_features;
- s->ivq = virtio_add_queue(&s->vdev, 128, virtio_console_handle_input);
- s->dvq = virtio_add_queue(&s->vdev, 128, virtio_console_handle_output);
- s->chr = chr;
- qemu_chr_add_handlers(chr, vcon_can_read, vcon_read, vcon_event, s);
- register_savevm("virtio-console", -1, 1, virtio_console_save, virtio_console_load, s);
- return &s->vdev;
- }
|