It isn't obvious what 'dvq' stands for. Since it's the output queue and the corresponding input queue is called 'ivq', call this 'ovq' Signed-off-by: Amit Shah <amit.shah@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
		
			
				
	
	
		
			144 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			144 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * 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, *ovq;
 | |
|     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, (uint8_t *)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;
 | |
| }
 | |
| 
 | |
| VirtIODevice *virtio_console_init(DeviceState *dev)
 | |
| {
 | |
|     VirtIOConsole *s;
 | |
|     s = (VirtIOConsole *)virtio_common_init("virtio-console",
 | |
|                                             VIRTIO_ID_CONSOLE,
 | |
|                                             0, sizeof(VirtIOConsole));
 | |
|     s->vdev.get_features = virtio_console_get_features;
 | |
| 
 | |
|     s->ivq = virtio_add_queue(&s->vdev, 128, virtio_console_handle_input);
 | |
|     s->ovq = virtio_add_queue(&s->vdev, 128, virtio_console_handle_output);
 | |
| 
 | |
|     s->chr = qdev_init_chardev(dev);
 | |
|     qemu_chr_add_handlers(s->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;
 | |
| }
 |