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 16 const char * 17 get_feature_name(uint64 feature) 18 { 19 switch (feature) { 20 } 21 return NULL; 22 } 23 24 25 VirtioRNGDevice::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, NULL); 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 86 VirtioRNGDevice::~VirtioRNGDevice() 87 { 88 cancel_timer(&fTimer); 89 gDPC->delete_dpc_queue(fDPCHandle); 90 91 } 92 93 94 status_t 95 VirtioRNGDevice::InitCheck() 96 { 97 return fStatus; 98 } 99 100 101 status_t 102 VirtioRNGDevice::_Enqueue() 103 { 104 CALLED(); 105 106 uint64 value = 0; 107 physical_entry entry; 108 get_memory_map(&value, sizeof(value), &entry, 1); 109 110 ConditionVariableEntry conditionVariableEntry; 111 { 112 InterruptsSpinLocker locker(fInterruptLock); 113 fExpectsInterrupt = true; 114 fInterruptCondition.Add(&conditionVariableEntry); 115 } 116 117 status_t result = fVirtio->queue_request(fVirtioQueue, NULL, &entry, NULL); 118 if (result != B_OK) { 119 ERROR("queueing failed (%s)\n", strerror(result)); 120 return result; 121 } 122 123 result = conditionVariableEntry.Wait(B_CAN_INTERRUPT); 124 125 { 126 InterruptsSpinLocker locker(fInterruptLock); 127 fExpectsInterrupt = false; 128 } 129 130 if (result == B_OK) { 131 if (value != 0) { 132 gRandom->queue_randomness(value); 133 TRACE("enqueue %" B_PRIx64 "\n", value); 134 } 135 } else if (result != B_OK && result != B_INTERRUPTED) { 136 ERROR("request failed (%s)\n", strerror(result)); 137 } 138 139 return result; 140 } 141 142 143 void 144 VirtioRNGDevice::_RequestCallback(void* driverCookie, void* cookie) 145 { 146 VirtioRNGDevice* device = (VirtioRNGDevice*)driverCookie; 147 148 while (device->fVirtio->queue_dequeue(device->fVirtioQueue, NULL, NULL)) 149 ; 150 151 device->_RequestInterrupt(); 152 } 153 154 155 void 156 VirtioRNGDevice::_RequestInterrupt() 157 { 158 SpinLocker locker(fInterruptLock); 159 fInterruptCondition.NotifyAll(); 160 } 161 162 163 /*static*/ int32 164 VirtioRNGDevice::HandleTimerHook(struct timer* timer) 165 { 166 VirtioRNGDevice* device = reinterpret_cast<VirtioRNGDevice*>(timer->user_data); 167 168 gDPC->queue_dpc(device->fDPCHandle, HandleDPC, device); 169 return B_HANDLED_INTERRUPT; 170 } 171 172 173 /*static*/ void 174 VirtioRNGDevice::HandleDPC(void *arg) 175 { 176 VirtioRNGDevice* device = reinterpret_cast<VirtioRNGDevice*>(arg); 177 device->_Enqueue(); 178 } 179 180