static int nsm_communicate_with_device()

in nsm-driver/nsm.c [213:302]


static int nsm_communicate_with_device(struct nsm_kernel_message *message)
{
	struct virtqueue *vq = NULL;
	struct scatterlist sg_in, sg_out;
	unsigned int len;
	void *queue_buf;
	bool kicked;
	int rc;

	if (!message)
		return -EINVAL;

	if (!nsm_dev)
		return -ENXIO;

	vq = nsm_get_vq(nsm_dev);
	if (!vq)
		return -ENXIO;

	/* Verify if buffer memory is valid. */
	if (!virt_addr_valid(message->request.iov_base) ||
		!virt_addr_valid(((u8 *)message->request.iov_base) +
			message->request.iov_len - 1) ||
		!virt_addr_valid(message->response.iov_base) ||
		!virt_addr_valid(((u8 *)message->response.iov_base) +
			message->response.iov_len - 1))
		return -EINVAL;

	/* Initialize scatter-gather lists with request and response buffers. */
	sg_init_one(&sg_out, message->request.iov_base,
		message->request.iov_len);
	sg_init_one(&sg_in, message->response.iov_base,
		message->response.iov_len);

	/* Add the request buffer (read by the device). */
	rc = virtqueue_add_outbuf(vq, &sg_out, 1, message->request.iov_base,
		GFP_KERNEL);
	if (rc)
		return rc;

	/* Add the response buffer (written by the device). */
	rc = virtqueue_add_inbuf(vq, &sg_in, 1, message->response.iov_base,
		GFP_KERNEL);
	if (rc)
		goto cleanup;

	nsm_device_notified = false;
	kicked = virtqueue_kick(vq);
	if (!kicked) {
		/* Cannot kick the virtqueue. */
		rc = -EIO;
		goto cleanup;
	}

	/* If the kick succeeded, wait for the device's response. */
	rc = wait_event_timeout(nsm_waitqueue,
		nsm_device_notified == true,
		msecs_to_jiffies(NSM_DEFAULT_TIMEOUT_MSECS));
	if (!rc) {
		rc = -ETIMEDOUT;
		goto cleanup;
	}

	queue_buf = virtqueue_get_buf(vq, &len);
	if (!queue_buf || (queue_buf != message->request.iov_base)) {
		pr_err("NSM device received wrong request buffer.");
		rc = -ENODATA;
		goto cleanup;
	}

	queue_buf = virtqueue_get_buf(vq, &len);
	if (!queue_buf || (queue_buf != message->response.iov_base)) {
		pr_err("NSM device received wrong response buffer.");
		rc = -ENODATA;
		goto cleanup;
	}

	/* Make sure the response length doesn't exceed the buffer capacity. */
	if (len < message->response.iov_len)
		message->response.iov_len = len;

	return 0;

cleanup:
	/* Clean the virtqueue. */
	while (virtqueue_get_buf(vq, &len) != NULL)
		;

	return rc;
}