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); 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 { 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 141 void 142 VirtioRNGDevice::_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 153 void 154 VirtioRNGDevice::_RequestInterrupt() 155 { 156 SpinLocker locker(fInterruptLock); 157 fInterruptCondition.NotifyAll(); 158 } 159 160 161 /*static*/ int32 162 VirtioRNGDevice::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 172 VirtioRNGDevice::HandleDPC(void *arg) 173 { 174 VirtioRNGDevice* device = reinterpret_cast<VirtioRNGDevice*>(arg); 175 device->_Enqueue(); 176 } 177 178