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(uint64 feature)
18{
19	switch (feature) {
20	}
21	return NULL;
22}
23
24
25VirtioRNGDevice::VirtioRNGDevice(device_node *node)
26	:
27	fVirtio(NULL),
28	fVirtioDevice(NULL),
29	fStatus(B_NO_INIT),
30	fExpectsInterrupt(false),
31	fDPCHandle(NULL)
32{
33	CALLED();
34
35	B_INITIALIZE_SPINLOCK(&fInterruptLock);
36	fInterruptCondition.Init(this, "virtio rng transfer");
37
38	// get the Virtio device from our parent's parent
39	device_node *virtioParent = gDeviceManager->get_parent_node(node);
40
41	gDeviceManager->get_driver(virtioParent, (driver_module_info **)&fVirtio,
42		(void **)&fVirtioDevice);
43	gDeviceManager->put_node(virtioParent);
44
45	fVirtio->negotiate_features(fVirtioDevice,
46		0, &fFeatures, &get_feature_name);
47
48	fStatus = fVirtio->alloc_queues(fVirtioDevice, 1, &fVirtioQueue);
49	if (fStatus != B_OK) {
50		ERROR("queue allocation failed (%s)\n", strerror(fStatus));
51		return;
52	}
53
54	fStatus = fVirtio->setup_interrupt(fVirtioDevice, NULL, this);
55	if (fStatus != B_OK) {
56		ERROR("interrupt setup failed (%s)\n", strerror(fStatus));
57		return;
58	}
59
60	fStatus = fVirtio->queue_setup_interrupt(fVirtioQueue, _RequestCallback,
61		this);
62	if (fStatus != B_OK) {
63		ERROR("queue interrupt setup failed (%s)\n", strerror(fStatus));
64		return;
65	}
66
67	fStatus = gDPC->new_dpc_queue(&fDPCHandle, "virtio_rng timer",
68		B_LOW_PRIORITY);
69	if (fStatus != B_OK) {
70		ERROR("dpc setup failed (%s)\n", strerror(fStatus));
71		return;
72	}
73
74	if (fStatus == B_OK) {
75		fTimer.user_data = this;
76		fStatus = add_timer(&fTimer, &HandleTimerHook, 300 * 1000 * 1000, B_PERIODIC_TIMER);
77		if (fStatus != B_OK)
78			ERROR("timer setup failed (%s)\n", strerror(fStatus));
79	}
80
81	// trigger also now
82	gDPC->queue_dpc(fDPCHandle, HandleDPC, this);
83}
84
85
86VirtioRNGDevice::~VirtioRNGDevice()
87{
88	cancel_timer(&fTimer);
89	gDPC->delete_dpc_queue(fDPCHandle);
90
91}
92
93
94status_t
95VirtioRNGDevice::InitCheck()
96{
97	return fStatus;
98}
99
100
101status_t
102VirtioRNGDevice::_Enqueue()
103{
104	CALLED();
105
106	uint64 value = 0;
107	physical_entry entry;
108	get_memory_map(&value, sizeof(value), &entry, 1);
109
110	{
111		InterruptsSpinLocker locker(fInterruptLock);
112		fExpectsInterrupt = true;
113		fInterruptCondition.Add(&fInterruptConditionEntry);
114	}
115	status_t result = fVirtio->queue_request(fVirtioQueue, NULL, &entry, NULL);
116	if (result != B_OK) {
117		ERROR("queueing failed (%s)\n", strerror(result));
118		return result;
119	}
120
121	result = fInterruptConditionEntry.Wait(B_CAN_INTERRUPT);
122
123	{
124		InterruptsSpinLocker locker(fInterruptLock);
125		fExpectsInterrupt = false;
126	}
127
128	if (result == B_OK) {
129		if (value != 0) {
130			gRandom->queue_randomness(value);
131			TRACE("enqueue %" B_PRIx64 "\n", value);
132		}
133	} else if (result != B_OK && result != B_INTERRUPTED) {
134		ERROR("request failed (%s)\n", strerror(result));
135	}
136
137	return result;
138}
139
140
141void
142VirtioRNGDevice::_RequestCallback(void* driverCookie, void* cookie)
143{
144	VirtioRNGDevice* device = (VirtioRNGDevice*)driverCookie;
145
146	while (device->fVirtio->queue_dequeue(device->fVirtioQueue, NULL, NULL))
147		;
148
149	device->_RequestInterrupt();
150}
151
152
153void
154VirtioRNGDevice::_RequestInterrupt()
155{
156	SpinLocker locker(fInterruptLock);
157	fInterruptCondition.NotifyAll();
158}
159
160
161/*static*/ int32
162VirtioRNGDevice::HandleTimerHook(struct timer* timer)
163{
164	VirtioRNGDevice* device = reinterpret_cast<VirtioRNGDevice*>(timer->user_data);
165
166	gDPC->queue_dpc(device->fDPCHandle, HandleDPC, device);
167	return B_HANDLED_INTERRUPT;
168}
169
170
171/*static*/ void
172VirtioRNGDevice::HandleDPC(void *arg)
173{
174	VirtioRNGDevice* device = reinterpret_cast<VirtioRNGDevice*>(arg);
175	device->_Enqueue();
176}
177
178