forked from vfp2/MeterFeeder
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdriver.cpp
297 lines (250 loc) · 9.29 KB
/
driver.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
/**
* MeterFeeder Library
*
* by fp2.dev
*/
#include "driver.h"
bool MeterFeeder::Driver::Initialize(string* errorReason) {
DWORD numDevices;
FT_STATUS ftdiStatus = FT_CreateDeviceInfoList(&numDevices);
if (ftdiStatus != FT_OK) {
makeErrorStr(errorReason, "Error creating device info list. Check if generators are connected. [%d]", ftdiStatus);
return false;
}
if (numDevices < 1) {
makeErrorStr(errorReason, "No generators connected", ftdiStatus);
return false;
}
vector<FT_DEVICE_LIST_INFO_NODE> devInfoList(numDevices);
ftdiStatus = FT_GetDeviceInfoList(&devInfoList[0], &numDevices);
if (ftdiStatus != FT_OK) {
makeErrorStr(errorReason, "Error getting the device info list");
return false;
}
_generators.clear();
// Open devices by serialNumber
for (DWORD i = 0; i < numDevices; i++) {
string serialNumber = devInfoList[i].SerialNumber;
serialNumber.resize(sizeof(devInfoList[i].SerialNumber));
FT_HANDLE ftHandle = devInfoList[i].ftHandle;
if (serialNumber.find("QWR") != 0) {
// Skip other but MED1K or MED100K and PQ128MU devices
continue;
}
// Open the current device
ftdiStatus = FT_OpenEx(&devInfoList[i].SerialNumber, FT_OPEN_BY_SERIAL_NUMBER, &ftHandle);
if (ftdiStatus != FT_OK) {
makeErrorStr(errorReason, "Failed to connect to %s", &serialNumber);
return false;
}
// Configure FTDI transport parameters
ftdiStatus = FT_SetLatencyTimer(ftHandle, FTDI_DEVICE_LATENCY_MS);
if (ftdiStatus != FT_OK) {
makeErrorStr(errorReason, "Failed to set latency time for %s", &serialNumber);
return false;
}
ftdiStatus = FT_SetUSBParameters(ftHandle, FTDI_DEVICE_PACKET_USB_SIZE_BYTES, FTDI_DEVICE_PACKET_USB_SIZE_BYTES);
if (ftdiStatus != FT_OK) {
makeErrorStr(errorReason, "Failed to set in/out packset size for %s", &serialNumber);
return false;
}
ftdiStatus = FT_SetTimeouts(ftHandle, FTDI_DEVICE_TX_TIMEOUT_MS, FTDI_DEVICE_TX_TIMEOUT_MS);
if (ftdiStatus != FT_OK) {
makeErrorStr(errorReason, "Failed to set timeout time for %s", &serialNumber);
return false;
}
// Device is successfully initialized. Add it to the list of generators the driver will control.
Generator generator = Generator(&devInfoList[i].SerialNumber[0], &devInfoList[i].Description[0], ftHandle);
_generators.push_back(generator);
}
return true;
};
void MeterFeeder::Driver::Shutdown() {
// Shutdown all generators
for (size_t i = 0; i < _generators.size(); i++) {
_generators[i].Close();
}
};
void MeterFeeder::Driver::Clear(FT_HANDLE handle, string* errorReason) {
// Find the specified generator
Generator *generator = FindGeneratorByHandle(handle);
if (!generator) {
makeErrorStr(errorReason, "Could not find %s by the handle %x", generator->GetSerialNumber().c_str(), generator->GetHandle());
return;
}
// Get the device to stop measuring randomness
FT_STATUS streamStatus = generator->StopStreaming();
if (streamStatus != FT_OK) {
makeErrorStr(errorReason, "Error instructing %s to stop streaming entropy [%d]", generator->GetSerialNumber().c_str(), streamStatus);
return;
}
};
int MeterFeeder::Driver::GetNumberGenerators() {
return _generators.size();
};
vector<MeterFeeder::Generator>* MeterFeeder::Driver::GetListGenerators() {
return &_generators;
};
void MeterFeeder::Driver::GetBytes(FT_HANDLE handle, int length, unsigned char* entropyBytes, string* errorReason) {
// Find the specified generator
Generator *generator = FindGeneratorByHandle(handle);
if (!generator) {
makeErrorStr(errorReason, "Could not find %s by the handle %x", generator->GetSerialNumber().c_str(), generator->GetHandle());
return;
}
// Get the device to start measuring randomness
FT_STATUS streamStatus = generator->StartStreaming();
if (streamStatus != FT_OK) {
makeErrorStr(errorReason, "Error instructing %s to start streaming entropy [%d]", generator->GetSerialNumber().c_str(), streamStatus);
return;
}
// Read in the entropy
FT_STATUS readStatus = generator->Read(length, entropyBytes);
if (readStatus != FT_OK) {
makeErrorStr(errorReason, "Error reading in entropy from %s [%d]", generator->GetSerialNumber().c_str(), readStatus);
return;
}
};
MeterFeeder::Generator* MeterFeeder::Driver::FindGeneratorByHandle(FT_HANDLE handle) {
for (size_t i = 0; i < _generators.size(); i++) {
if (_generators[i].GetHandle() == handle) {
return &_generators[i];
}
}
return nullptr;
};
MeterFeeder::Generator* MeterFeeder::Driver::FindGeneratorBySerial(string serialNumber) {
for (size_t i = 0; i < _generators.size(); i++) {
if (_generators[i].GetSerialNumber() == serialNumber) {
return &_generators[i];
}
}
return nullptr;
};
void MeterFeeder::Driver::makeErrorStr(string* errorReason, const char* format, ...) {
char buffer[MF_ERROR_STR_MAX_LEN];
va_list args;
va_start (args, format);
vsnprintf (buffer, MF_ERROR_STR_MAX_LEN-1, format, args);
*errorReason = buffer;
va_end (args);
};
/**
* Code below for interfacing with MeterFeeder as a library
*/
#ifdef __APPLE__
#define DllExport __attribute__((visibility("default")))
#elif defined __GNUC__
#define DllExport __attribute__((visibility("default")))
#else
#define DllExport __declspec(dllexport)
#endif
extern "C" {
/**
* Library functions exposed for consumption by Unity/C# or whomever
*/
using namespace MeterFeeder;
Driver driver = Driver();
// Initialize the connected generators
DllExport int MF_Initialize(char* pErrorReason) {
string errorReason = "";
int res = driver.Initialize(&errorReason);
std::strcpy(pErrorReason, errorReason.c_str());
return res;
}
// Shutdown and de-initialize all the generators.
DllExport void MF_Shutdown() {
driver.Shutdown();
}
// Shutdown and re-initialize all the generators.
DllExport int MF_Reset(char* pErrorReason) {
MF_Shutdown();
return MF_Initialize(pErrorReason);
}
// Stop streaming on the specified generator.
DllExport bool MF_Clear(char* generatorSerialNumber, char* pErrorReason) {
string errorReason = "";
Generator *generator = driver.FindGeneratorBySerial(generatorSerialNumber);
driver.Clear(generator->GetHandle(), &errorReason);
std::strcpy(pErrorReason, errorReason.c_str());
if (*pErrorReason != '\0') {
return false;
}
return true;
}
// Get the number of connected and successfully initialized generators.
DllExport int MF_GetNumberGenerators() {
return driver.GetNumberGenerators();
}
// Get the list of connected and successfully initialized generators.
// Array element format: <serial number>|<description>
DllExport void MF_GetListGenerators(char** pGenerators) {
vector<Generator>* generators = driver.GetListGenerators();
for (int i = 0; i < driver.GetNumberGenerators(); i++) {
Generator generator = generators->at(i);
string fullGenDesc = generator.GetSerialNumber() + "|" + generator.GetDescription();
std::strcpy(pGenerators[i], fullGenDesc.c_str());
}
}
// Get bytes of randomness.
DllExport void MF_GetBytes(int length, unsigned char* buffer, char* generatorSerialNumber, char* pErrorReason) {
string errorReason = "";
Generator *generator = driver.FindGeneratorBySerial(generatorSerialNumber);
driver.GetBytes(generator->GetHandle(), length, buffer, &errorReason);
std::strcpy(pErrorReason, errorReason.c_str());
}
// Get a byte of randomness.
DllExport unsigned char MF_GetByte(char* generatorSerialNumber, char* pErrorReason) {
unsigned char byte;
MF_GetBytes(1, &byte, generatorSerialNumber, pErrorReason);
return byte;
}
// Get a random 32 bit integer.
DllExport int32_t MF_RandInt32(char* generatorSerialNumber, char* pErrorReason) {
UCHAR *buffer = (UCHAR*)malloc(sizeof(int32_t));
MF_GetBytes(sizeof(int32_t), buffer, generatorSerialNumber, pErrorReason);
int32_t rc = 0;
// (little endianess assumed)
for (int i = 0; i < sizeof(int32_t); i++) {
((unsigned char*)&rc)[i] = buffer[i];
}
return rc;
}
// Get a random floating point number between [0,1)
DllExport double MF_RandUniform(char* generatorSerialNumber, char* pErrorReason) {
int sizeofUint48 = 6; // value for the mantissa part of double
UCHAR *buffer = (UCHAR*)malloc(sizeof(sizeofUint48));
MF_GetBytes(sizeofUint48, buffer, generatorSerialNumber, pErrorReason);
uint64_t mantissa = 0;
// (little endianess assumed)
for (int i = 0; i < sizeofUint48; i++) {
((unsigned char*)&mantissa)[i] = buffer[i];
}
// copy 6 bytes into mantissa
double uniform = (double)mantissa;
uniform /= 281474976710656.0; // 2^(6*8)
return uniform;
}
// Get a random normal number with mean zero and standard deviation one
DllExport double MF_RandNormal(char* generatorSerialNumber, char* pErrorReason) {
// TODO: Not confident in the accuracy of the implementation here - should be checked one day
// first half of calculation: create normU1
double normU1 = MF_RandUniform(generatorSerialNumber, pErrorReason);
if (*pErrorReason != '\0') {
return 0;
}
normU1 += FTDI_DEVICE_HALF_OF_UNIFORM_LSB;
// second half: create normU2
double normU2 = MF_RandUniform(generatorSerialNumber, pErrorReason);
if (*pErrorReason != '\0') {
return 0;
}
normU2 += FTDI_DEVICE_HALF_OF_UNIFORM_LSB;
// n1 = cos(2PI * u2) * sqrt(-2 * ln(u1))
// n2 = sin(2PI * u2) * sqrt(-2 * ln(u1))
double sqrtTerm = sqrt(-2.0 * log(normU1));
double normal = cos(FTDI_DEVICE_2_PI * normU2) * sqrtTerm;
//normalConjugate = sin(FTDI_DEVICE_2_PI * normU2) * sqrtTerm;
return normal;
}
}