1 #include "globals.h"
2 #include "usb.h"
3 #include "protocol.h"
4 #include "fluxmap.h"
5 #include "bytes.h"
6 #include <libusb.h>
7 #include "fmt/format.h"
8 
9 #define TIMEOUT 5000
10 
11 /* Hacky: the board always operates in little-endian mode. */
read_short_from_usb(uint16_t usb)12 static uint16_t read_short_from_usb(uint16_t usb)
13 {
14 	uint8_t* p = (uint8_t*)&usb;
15 	return p[0] | (p[1] << 8);
16 }
17 
18 class FluxEngineUsb : public USB
19 {
20 private:
21 	uint8_t _buffer[FRAME_SIZE];
22 
usb_cmd_send(void * ptr,int len)23 	int usb_cmd_send(void* ptr, int len)
24 	{
25 		//std::cerr << "send:\n";
26 		//hexdump(std::cerr, Bytes((const uint8_t*)ptr, len));
27 		int i = libusb_interrupt_transfer(_device, FLUXENGINE_CMD_OUT_EP,
28 			(uint8_t*) ptr, len, &len, TIMEOUT);
29 		if (i < 0)
30 			Error() << "failed to send command: " << usberror(i);
31 		return len;
32 	}
33 
usb_cmd_recv(void * ptr,int len)34 	void usb_cmd_recv(void* ptr, int len)
35 	{
36 		int i = libusb_interrupt_transfer(_device, FLUXENGINE_CMD_IN_EP,
37 		   (uint8_t*)  ptr, len, &len, TIMEOUT);
38 		if (i < 0)
39 			Error() << "failed to receive command reply: " << usberror(i);
40 		//std::cerr << "recv:\n";
41 		//hexdump(std::cerr, Bytes((const uint8_t*)ptr, len));
42 	}
43 
large_bulk_transfer(int ep,Bytes & bytes)44 	int large_bulk_transfer(int ep, Bytes& bytes)
45 	{
46 		if (bytes.size() == 0)
47 			return 0;
48 
49 		int len;
50 		int i = libusb_bulk_transfer(_device, ep, bytes.begin(), bytes.size(), &len, TIMEOUT);
51 		if (i < 0)
52 			Error() << fmt::format("data transfer failed at {} bytes: {}", len, usberror(i));
53 		return len;
54 	}
55 
56 public:
FluxEngineUsb(libusb_device_handle * device)57 	FluxEngineUsb(libusb_device_handle* device)
58 	{
59 		_device = device;
60 
61 		int i;
62 		int cfg = -1;
63 		libusb_get_configuration(_device, &cfg);
64 		if (cfg != 1)
65 		{
66 			i = libusb_set_configuration(_device, 1);
67 			if (i < 0)
68 				Error() << "the FluxEngine would not accept configuration: " << usberror(i);
69 		}
70 
71 		i = libusb_claim_interface(_device, 0);
72 		if (i < 0)
73 			Error() << "could not claim interface: " << usberror(i);
74 
75 		int version = getVersion();
76 		if (version != FLUXENGINE_VERSION)
77 			Error() << "your FluxEngine firmware is at version " << version
78 					<< " but the client is for version " << FLUXENGINE_VERSION
79 					<< "; please upgrade";
80 	}
81 
82 private:
bad_reply(void)83 	void bad_reply(void)
84 	{
85 		struct error_frame* f = (struct error_frame*) _buffer;
86 		if (f->f.type != F_FRAME_ERROR)
87 			Error() << fmt::format("bad USB reply 0x{:2x}", f->f.type);
88 		switch (f->error)
89 		{
90 			case F_ERROR_BAD_COMMAND:
91 				Error() << "device did not understand command";
92 
93 			case F_ERROR_UNDERRUN:
94 				Error() << "USB underrun (not enough bandwidth)";
95 
96 			default:
97 				Error() << fmt::format("unknown device error {}", f->error);
98 		}
99 	}
100 
101 	template <typename T>
await_reply(int desired)102 	T* await_reply(int desired)
103 	{
104 		for (;;)
105 		{
106 			usb_cmd_recv(_buffer, sizeof(_buffer));
107 			struct any_frame* r = (struct any_frame*) _buffer;
108 			if (r->f.type == F_FRAME_DEBUG)
109 			{
110 				std::cout << "dev: " << ((struct debug_frame*)r)->payload << std::endl;
111 				continue;
112 			}
113 			if (r->f.type != desired)
114 				bad_reply();
115 			return (T*) r;
116 		}
117 	}
118 
119 public:
getVersion()120 	int getVersion()
121 	{
122 		struct any_frame f = { .f = {.type = F_FRAME_GET_VERSION_CMD, .size = sizeof(f)} };
123 		usb_cmd_send(&f, f.f.size);
124 		auto r = await_reply<struct version_frame>(F_FRAME_GET_VERSION_REPLY);
125 		return r->version;
126 	}
127 
seek(int track)128 	void seek(int track)
129 	{
130 		struct seek_frame f = {
131 			{ .type = F_FRAME_SEEK_CMD, .size = sizeof(f) },
132 			.track = (uint8_t) track
133 		};
134 		usb_cmd_send(&f, f.f.size);
135 		await_reply<struct any_frame>(F_FRAME_SEEK_REPLY);
136 	}
137 
recalibrate()138 	void recalibrate()
139 	{
140 		struct any_frame f = {
141 			{ .type = F_FRAME_RECALIBRATE_CMD, .size = sizeof(f) },
142 		};
143 		usb_cmd_send(&f, f.f.size);
144 		await_reply<struct any_frame>(F_FRAME_RECALIBRATE_REPLY);
145 	}
146 
getRotationalPeriod(int hardSectorCount)147 	nanoseconds_t getRotationalPeriod(int hardSectorCount)
148 	{
149 		struct measurespeed_frame f = {
150 			.f = {.type = F_FRAME_MEASURE_SPEED_CMD, .size = sizeof(f)},
151 			.hard_sector_count = (uint8_t) hardSectorCount,
152 		};
153 		usb_cmd_send(&f, f.f.size);
154 
155 		auto r = await_reply<struct speed_frame>(F_FRAME_MEASURE_SPEED_REPLY);
156 		return r->period_ms * 1000000;
157 	}
158 
testBulkWrite()159 	void testBulkWrite()
160 	{
161 		struct any_frame f = { .f = {.type = F_FRAME_BULK_WRITE_TEST_CMD, .size = sizeof(f)} };
162 		usb_cmd_send(&f, f.f.size);
163 
164 		/* These must match the device. */
165 		const int XSIZE = 64;
166 		const int YSIZE = 256;
167 		const int ZSIZE = 64;
168 
169 		Bytes bulk_buffer(XSIZE*YSIZE*ZSIZE);
170 		double start_time = getCurrentTime();
171 		large_bulk_transfer(FLUXENGINE_DATA_IN_EP, bulk_buffer);
172 		double elapsed_time = getCurrentTime() - start_time;
173 
174 		std::cout << "Transferred "
175 				  << bulk_buffer.size()
176 				  << " bytes from FluxEngine -> PC in "
177 				  << int(elapsed_time * 1000.0)
178 				  << " ms ("
179 				  << int((bulk_buffer.size() / 1024.0) / elapsed_time)
180 				  << " kB/s)"
181 				  << std::endl;
182 
183 		for (int x=0; x<XSIZE; x++)
184 			for (int y=0; y<YSIZE; y++)
185 				for (int z=0; z<ZSIZE; z++)
186 				{
187 					int offset = x*XSIZE*YSIZE + y*ZSIZE + z;
188 					if (bulk_buffer[offset] != uint8_t(x+y+z))
189 						Error() << "data transfer corrupted at 0x"
190 								<< std::hex << offset << std::dec
191 								<< " "
192 								<< x << '.' << y << '.' << z << '.';
193 				}
194 
195 		await_reply<struct any_frame>(F_FRAME_BULK_WRITE_TEST_REPLY);
196 	}
197 
testBulkRead()198 	void testBulkRead()
199 	{
200 		struct any_frame f = { .f = {.type = F_FRAME_BULK_READ_TEST_CMD, .size = sizeof(f)} };
201 		usb_cmd_send(&f, f.f.size);
202 
203 		/* These must match the device. */
204 		const int XSIZE = 64;
205 		const int YSIZE = 256;
206 		const int ZSIZE = 64;
207 
208 		Bytes bulk_buffer(XSIZE*YSIZE*ZSIZE);
209 		for (int x=0; x<XSIZE; x++)
210 			for (int y=0; y<YSIZE; y++)
211 				for (int z=0; z<ZSIZE; z++)
212 				{
213 					int offset = x*XSIZE*YSIZE + y*ZSIZE + z;
214 					bulk_buffer[offset] = uint8_t(x+y+z);
215 				}
216 
217 		double start_time = getCurrentTime();
218 		large_bulk_transfer(FLUXENGINE_DATA_OUT_EP, bulk_buffer);
219 		double elapsed_time = getCurrentTime() - start_time;
220 
221 		std::cout << "Transferred "
222 				  << bulk_buffer.size()
223 				  << " bytes from PC -> FluxEngine in "
224 				  << int(elapsed_time * 1000.0)
225 				  << " ms ("
226 				  << int((bulk_buffer.size() / 1024.0) / elapsed_time)
227 				  << " kB/s)"
228 				  << std::endl;
229 
230 		await_reply<struct any_frame>(F_FRAME_BULK_READ_TEST_REPLY);
231 	}
232 
read(int side,bool synced,nanoseconds_t readTime,nanoseconds_t hardSectorThreshold)233 	Bytes read(int side, bool synced, nanoseconds_t readTime,
234 	           nanoseconds_t hardSectorThreshold)
235 	{
236 		struct read_frame f = {
237 			.f = { .type = F_FRAME_READ_CMD, .size = sizeof(f) },
238 			.side = (uint8_t) side,
239 			.synced = (uint8_t) synced,
240 		};
241 		f.hardsec_threshold_ms = (hardSectorThreshold + 5e5) / 1e6; /* round to nearest ms */
242 		uint16_t milliseconds = readTime / 1e6;
243 		((uint8_t*)&f.milliseconds)[0] = milliseconds;
244 		((uint8_t*)&f.milliseconds)[1] = milliseconds >> 8;
245 		usb_cmd_send(&f, f.f.size);
246 
247 		auto fluxmap = std::unique_ptr<Fluxmap>(new Fluxmap);
248 
249 		Bytes buffer(1024*1024);
250 		int len = large_bulk_transfer(FLUXENGINE_DATA_IN_EP, buffer);
251 		buffer.resize(len);
252 
253 		await_reply<struct any_frame>(F_FRAME_READ_REPLY);
254 		return buffer;
255 	}
256 
write(int side,const Bytes & bytes,nanoseconds_t hardSectorThreshold)257 	void write(int side, const Bytes& bytes, nanoseconds_t hardSectorThreshold)
258 	{
259 		unsigned safelen = bytes.size() & ~(FRAME_SIZE-1);
260 		Bytes safeBytes = bytes.slice(0, safelen);
261 
262 		struct write_frame f = {
263 			.f = { .type = F_FRAME_WRITE_CMD, .size = sizeof(f) },
264 			.side = (uint8_t) side,
265 		};
266 		f.hardsec_threshold_ms = (hardSectorThreshold + 5e5) / 1e6; /* round to nearest ms */
267 		((uint8_t*)&f.bytes_to_write)[0] = safelen;
268 		((uint8_t*)&f.bytes_to_write)[1] = safelen >> 8;
269 		((uint8_t*)&f.bytes_to_write)[2] = safelen >> 16;
270 		((uint8_t*)&f.bytes_to_write)[3] = safelen >> 24;
271 
272 		usb_cmd_send(&f, f.f.size);
273 		large_bulk_transfer(FLUXENGINE_DATA_OUT_EP, safeBytes);
274 
275 		await_reply<struct any_frame>(F_FRAME_WRITE_REPLY);
276 	}
277 
erase(int side,nanoseconds_t hardSectorThreshold)278 	void erase(int side, nanoseconds_t hardSectorThreshold)
279 	{
280 		struct erase_frame f = {
281 			.f = { .type = F_FRAME_ERASE_CMD, .size = sizeof(f) },
282 			.side = (uint8_t) side,
283 		};
284 		f.hardsec_threshold_ms = (hardSectorThreshold + 5e5) / 1e6; /* round to nearest ms */
285 		usb_cmd_send(&f, f.f.size);
286 
287 		await_reply<struct any_frame>(F_FRAME_ERASE_REPLY);
288 	}
289 
setDrive(int drive,bool high_density,int index_mode)290 	void setDrive(int drive, bool high_density, int index_mode)
291 	{
292 		struct set_drive_frame f = {
293 			{ .type = F_FRAME_SET_DRIVE_CMD, .size = sizeof(f) },
294 			.drive = (uint8_t) drive,
295 			.high_density = high_density,
296 			.index_mode = (uint8_t) index_mode
297 		};
298 		usb_cmd_send(&f, f.f.size);
299 		await_reply<struct any_frame>(F_FRAME_SET_DRIVE_REPLY);
300 	}
301 
measureVoltages(struct voltages_frame * voltages)302 	void measureVoltages(struct voltages_frame* voltages)
303 	{
304 		struct any_frame f = {
305 			{ .type = F_FRAME_MEASURE_VOLTAGES_CMD, .size = sizeof(f) },
306 		};
307 		usb_cmd_send(&f, f.f.size);
308 
309 		auto convert_voltages_from_usb = [&](const struct voltages& vin, struct voltages& vout)
310 		{
311 			vout.logic0_mv = read_short_from_usb(vin.logic0_mv);
312 			vout.logic1_mv = read_short_from_usb(vin.logic1_mv);
313 		};
314 
315 		struct voltages_frame* r = await_reply<struct voltages_frame>(F_FRAME_MEASURE_VOLTAGES_REPLY);
316 		convert_voltages_from_usb(r->input_both_off, voltages->input_both_off);
317 		convert_voltages_from_usb(r->input_drive_0_selected, voltages->input_drive_0_selected);
318 		convert_voltages_from_usb(r->input_drive_1_selected, voltages->input_drive_1_selected);
319 		convert_voltages_from_usb(r->input_drive_0_running, voltages->input_drive_0_running);
320 		convert_voltages_from_usb(r->input_drive_1_running, voltages->input_drive_1_running);
321 		convert_voltages_from_usb(r->output_both_off, voltages->output_both_off);
322 		convert_voltages_from_usb(r->output_drive_0_selected, voltages->output_drive_0_selected);
323 		convert_voltages_from_usb(r->output_drive_1_selected, voltages->output_drive_1_selected);
324 		convert_voltages_from_usb(r->output_drive_0_running, voltages->output_drive_0_running);
325 		convert_voltages_from_usb(r->output_drive_1_running, voltages->output_drive_1_running);
326 	}
327 };
328 
createFluxengineUsb(libusb_device_handle * device)329 USB* createFluxengineUsb(libusb_device_handle* device)
330 {
331 	return new FluxEngineUsb(device);
332 }
333 
334