| /* | 
 |  * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com> | 
 |  * | 
 |  * This work is licensed under the terms of the GNU LGPL, version 2. | 
 |  */ | 
 | #include "libcflat.h" | 
 | #include "virtio.h" | 
 | #include "asm/spinlock.h" | 
 |  | 
 | #include "chr-testdev.h" | 
 |  | 
 | #define TESTDEV_NAME "chr-testdev" | 
 |  | 
 | static struct virtio_device *vcon; | 
 | static struct virtqueue *in_vq, *out_vq; | 
 | static struct spinlock lock; | 
 |  | 
 | static void __testdev_send(char *buf, unsigned int len) | 
 | { | 
 | 	int ret; | 
 |  | 
 | 	ret = virtqueue_add_outbuf(out_vq, buf, len); | 
 | 	virtqueue_kick(out_vq); | 
 |  | 
 | 	if (ret < 0) | 
 | 		return; | 
 |  | 
 | 	while (!virtqueue_get_buf(out_vq, &len)) | 
 | 		; | 
 | } | 
 |  | 
 | void chr_testdev_exit(int code) | 
 | { | 
 | 	unsigned int len; | 
 | 	static char buf[8]; | 
 |  | 
 | 	spin_lock(&lock); | 
 | 	if (!vcon) | 
 | 		goto out; | 
 |  | 
 | 	snprintf(buf, sizeof(buf), "%dq", code); | 
 | 	len = strlen(buf); | 
 |  | 
 | 	__testdev_send(buf, len); | 
 |  | 
 | out: | 
 | 	spin_unlock(&lock); | 
 | } | 
 |  | 
 | void chr_testdev_init(void) | 
 | { | 
 | 	const char *io_names[] = { "input", "output" }; | 
 | 	struct virtqueue *vqs[2]; | 
 | 	int ret; | 
 |  | 
 | 	vcon = virtio_bind(VIRTIO_ID_CONSOLE); | 
 | 	if (vcon == NULL) | 
 | 		return; | 
 |  | 
 | 	ret = vcon->config->find_vqs(vcon, 2, vqs, NULL, io_names); | 
 | 	if (ret < 0) { | 
 | 		printf("%s: %s: can't init virtqueues\n", | 
 | 				__func__, TESTDEV_NAME); | 
 | 		vcon = NULL; | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	in_vq = vqs[0]; | 
 | 	out_vq = vqs[1]; | 
 | } |