1/*
2 * Copyright 2013, J��r��me Duval, korli@users.berlios.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "VirtioRNGPrivate.h"
8
9#include <new>
10#include <stdlib.h>
11#include <string.h>
12
13#include <util/AutoLock.h>
14
15
16const char *
17get_feature_name(uint32 feature)
18{
19	switch (feature) {
20	}
21	return NULL;
22}
23
24
25VirtioRNGDevice::VirtioRNGDevice(device_node *node)
26	:
27	fNode(node),
28	fVirtio(NULL),
29	fVirtioDevice(NULL),
30	fStatus(B_NO_INIT),
31	fOffset(BUFFER_SIZE),
32	fExpectsInterrupt(false)
33{
34	CALLED();
35
36	B_INITIALIZE_SPINLOCK(&fInterruptLock);
37	fInterruptCondition.Init(this, "virtio rng transfer");
38
39	get_memory_map(fBuffer, BUFFER_SIZE, &fEntry, 1);
40
41	// get the Virtio device from our parent's parent
42	device_node *parent = gDeviceManager->get_parent_node(node);
43	device_node *virtioParent = gDeviceManager->get_parent_node(parent);
44	gDeviceManager->put_node(parent);
45
46	gDeviceManager->get_driver(virtioParent, (driver_module_info **)&fVirtio,
47		(void **)&fVirtioDevice);
48	gDeviceManager->put_node(virtioParent);
49
50	fVirtio->negotiate_features(fVirtioDevice,
51		0, &fFeatures, &get_feature_name);
52
53	fStatus = fVirtio->alloc_queues(fVirtioDevice, 1, &fVirtioQueue);
54	if (fStatus != B_OK) {
55		ERROR("queue allocation failed (%s)\n", strerror(fStatus));
56		return;
57	}
58
59	fStatus = fVirtio->setup_interrupt(fVirtioDevice, NULL, this);
60	if (fStatus != B_OK) {
61		ERROR("interrupt setup failed (%s)\n", strerror(fStatus));
62		return;
63	}
64
65	fStatus = fVirtio->queue_setup_interrupt(fVirtioQueue, _RequestCallback,
66		this);
67	if (fStatus != B_OK) {
68		ERROR("queue interrupt setup failed (%s)\n", strerror(fStatus));
69		return;
70	}
71}
72
73
74VirtioRNGDevice::~VirtioRNGDevice()
75{
76}
77
78
79status_t
80VirtioRNGDevice::InitCheck()
81{
82	return fStatus;
83}
84
85
86status_t
87VirtioRNGDevice::Read(void* _buffer, size_t* _numBytes)
88{
89	CALLED();
90
91	if (fOffset >= BUFFER_SIZE) {
92		{
93			InterruptsSpinLocker locker(fInterruptLock);
94			fExpectsInterrupt = true;
95			fInterruptCondition.Add(&fInterruptConditionEntry);
96		}
97		status_t result = fVirtio->queue_request(fVirtioQueue, NULL, &fEntry,
98			NULL);
99		if (result != B_OK) {
100			ERROR("queueing failed (%s)\n", strerror(result));
101			return result;
102		}
103
104		result = fInterruptConditionEntry.Wait(B_CAN_INTERRUPT);
105
106		{
107			InterruptsSpinLocker locker(fInterruptLock);
108			fExpectsInterrupt = false;
109		}
110
111		if (result == B_OK) {
112			fOffset = 0;
113		} else if (result != B_INTERRUPTED) {
114			ERROR("request failed (%s)\n", strerror(result));
115		}
116	}
117
118	if (fOffset < BUFFER_SIZE) {
119		size_t size = min_c(BUFFER_SIZE - fOffset, *_numBytes);
120		memcpy(_buffer, fBuffer + fOffset, size);
121		fOffset += size;
122		*_numBytes = size;
123	} else
124		*_numBytes = 0;
125	return B_OK;
126}
127
128
129void
130VirtioRNGDevice::_RequestCallback(void* driverCookie, void* cookie)
131{
132	VirtioRNGDevice* device = (VirtioRNGDevice*)driverCookie;
133
134	while (device->fVirtio->queue_dequeue(device->fVirtioQueue, NULL, NULL))
135		;
136
137	device->_RequestInterrupt();
138}
139
140
141void
142VirtioRNGDevice::_RequestInterrupt()
143{
144	SpinLocker locker(fInterruptLock);
145	fInterruptCondition.NotifyAll();
146}
147