mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
Merge pull request #182 from davidgiven/usb
Add support for multiple simultaneously connected FluxEngines.
This commit is contained in:
15
doc/using.md
15
doc/using.md
@@ -34,6 +34,21 @@ interchangeably: you can specify either at (very nearly) any time. A very
|
||||
common workflow is to read a disk to a flux file, and then reread from the flux
|
||||
file while changing the decoder options, to save disk wear. It's also much faster.
|
||||
|
||||
### Connecting it up
|
||||
|
||||
To use, simply plug your FluxEngine into your computer and run the client. If a
|
||||
single device is plugged in, it will be automatically detected and used.
|
||||
|
||||
If _more_ than one device is plugged in, you need to specify which one to use
|
||||
with the `--device` parameter, which takes the device serial number as a
|
||||
parameter. You can find out the serial numbers by running the command without
|
||||
the `--device` parameter, and if more than one device is attached they will be
|
||||
listed. The serial number is also shown whenever a connection is made.
|
||||
|
||||
You _can_ work with more than one FluxEngine at the same time, using different
|
||||
invocations of the client; but be careful of USB bandwidth. If the devices are
|
||||
connected via the same hub, the bandwidth will be shared.
|
||||
|
||||
### Source and destination specifiers
|
||||
|
||||
When reading from or writing _flux_ (either from or to a real disk, or a flux
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
#include "globals.h"
|
||||
#include "flags.h"
|
||||
#include "fluxmap.h"
|
||||
#include "usb.h"
|
||||
#include "usb/usb.h"
|
||||
#include "fluxsink/fluxsink.h"
|
||||
|
||||
FlagGroup hardwareFluxSinkFlags;
|
||||
FlagGroup hardwareFluxSinkFlags = {
|
||||
&usbFlags,
|
||||
};
|
||||
|
||||
static bool high_density = false;
|
||||
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
#include "globals.h"
|
||||
#include "flags.h"
|
||||
#include "fluxmap.h"
|
||||
#include "usb.h"
|
||||
#include "usb/usb.h"
|
||||
#include "fluxsource/fluxsource.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
FlagGroup hardwareFluxSourceFlags;
|
||||
FlagGroup hardwareFluxSourceFlags = {
|
||||
&usbFlags
|
||||
};
|
||||
|
||||
static DoubleFlag revolutions(
|
||||
{ "--revolutions" },
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "globals.h"
|
||||
#include "flags.h"
|
||||
#include "usb.h"
|
||||
#include "usb/usb.h"
|
||||
#include "fluxsource/fluxsource.h"
|
||||
#include "fluxsink/fluxsink.h"
|
||||
#include "reader.h"
|
||||
|
||||
344
lib/usb.cc
344
lib/usb.cc
@@ -1,344 +0,0 @@
|
||||
#include "globals.h"
|
||||
#include "usb.h"
|
||||
#include "protocol.h"
|
||||
#include "fluxmap.h"
|
||||
#include "bytes.h"
|
||||
#include <libusb.h>
|
||||
#include "fmt/format.h"
|
||||
|
||||
#define TIMEOUT 5000
|
||||
|
||||
static libusb_device_handle* device;
|
||||
|
||||
static uint8_t buffer[FRAME_SIZE];
|
||||
|
||||
static std::string usberror(int i)
|
||||
{
|
||||
return libusb_strerror((libusb_error) i);
|
||||
}
|
||||
|
||||
static void usb_init()
|
||||
{
|
||||
if (device)
|
||||
return;
|
||||
|
||||
int i = libusb_init(NULL);
|
||||
if (i < 0)
|
||||
Error() << "could not start libusb: " << usberror(i);
|
||||
|
||||
device = libusb_open_device_with_vid_pid(NULL, FLUXENGINE_VID, FLUXENGINE_PID);
|
||||
if (!device)
|
||||
Error() << "cannot find the FluxEngine (is it plugged in?)";
|
||||
|
||||
int cfg = -1;
|
||||
libusb_get_configuration(device, &cfg);
|
||||
if (cfg != 1)
|
||||
{
|
||||
i = libusb_set_configuration(device, 1);
|
||||
if (i < 0)
|
||||
Error() << "the FluxEngine would not accept configuration: " << usberror(i);
|
||||
}
|
||||
|
||||
i = libusb_claim_interface(device, 0);
|
||||
if (i < 0)
|
||||
Error() << "could not claim interface: " << usberror(i);
|
||||
|
||||
int version = usbGetVersion();
|
||||
if (version != FLUXENGINE_VERSION)
|
||||
Error() << "your FluxEngine firmware is at version " << version
|
||||
<< " but the client is for version " << FLUXENGINE_VERSION
|
||||
<< "; please upgrade";
|
||||
}
|
||||
|
||||
static int usb_cmd_send(void* ptr, int len)
|
||||
{
|
||||
//std::cerr << "send:\n";
|
||||
//hexdump(std::cerr, Bytes((const uint8_t*)ptr, len));
|
||||
int i = libusb_interrupt_transfer(device, FLUXENGINE_CMD_OUT_EP,
|
||||
(uint8_t*) ptr, len, &len, TIMEOUT);
|
||||
if (i < 0)
|
||||
Error() << "failed to send command: " << usberror(i);
|
||||
return len;
|
||||
}
|
||||
|
||||
void usb_cmd_recv(void* ptr, int len)
|
||||
{
|
||||
int i = libusb_interrupt_transfer(device, FLUXENGINE_CMD_IN_EP,
|
||||
(uint8_t*) ptr, len, &len, TIMEOUT);
|
||||
if (i < 0)
|
||||
Error() << "failed to receive command reply: " << usberror(i);
|
||||
//std::cerr << "recv:\n";
|
||||
//hexdump(std::cerr, Bytes((const uint8_t*)ptr, len));
|
||||
}
|
||||
|
||||
static void bad_reply(void)
|
||||
{
|
||||
struct error_frame* f = (struct error_frame*) buffer;
|
||||
if (f->f.type != F_FRAME_ERROR)
|
||||
Error() << fmt::format("bad USB reply 0x{:2x}", f->f.type);
|
||||
switch (f->error)
|
||||
{
|
||||
case F_ERROR_BAD_COMMAND:
|
||||
Error() << "device did not understand command";
|
||||
|
||||
case F_ERROR_UNDERRUN:
|
||||
Error() << "USB underrun (not enough bandwidth)";
|
||||
|
||||
default:
|
||||
Error() << fmt::format("unknown device error {}", f->error);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static T* await_reply(int desired)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
usb_cmd_recv(buffer, sizeof(buffer));
|
||||
struct any_frame* r = (struct any_frame*) buffer;
|
||||
if (r->f.type == F_FRAME_DEBUG)
|
||||
{
|
||||
std::cout << "dev: " << ((struct debug_frame*)r)->payload << std::endl;
|
||||
continue;
|
||||
}
|
||||
if (r->f.type != desired)
|
||||
bad_reply();
|
||||
return (T*) r;
|
||||
}
|
||||
}
|
||||
|
||||
int usbGetVersion(void)
|
||||
{
|
||||
usb_init();
|
||||
|
||||
struct any_frame f = { .f = {.type = F_FRAME_GET_VERSION_CMD, .size = sizeof(f)} };
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
auto r = await_reply<struct version_frame>(F_FRAME_GET_VERSION_REPLY);
|
||||
return r->version;
|
||||
}
|
||||
|
||||
void usbSeek(int track)
|
||||
{
|
||||
usb_init();
|
||||
|
||||
struct seek_frame f = {
|
||||
{ .type = F_FRAME_SEEK_CMD, .size = sizeof(f) },
|
||||
.track = (uint8_t) track
|
||||
};
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
await_reply<struct any_frame>(F_FRAME_SEEK_REPLY);
|
||||
}
|
||||
|
||||
void usbRecalibrate()
|
||||
{
|
||||
usb_init();
|
||||
|
||||
struct any_frame f = {
|
||||
{ .type = F_FRAME_RECALIBRATE_CMD, .size = sizeof(f) },
|
||||
};
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
await_reply<struct any_frame>(F_FRAME_RECALIBRATE_REPLY);
|
||||
}
|
||||
|
||||
nanoseconds_t usbGetRotationalPeriod(void)
|
||||
{
|
||||
usb_init();
|
||||
|
||||
struct any_frame f = { .f = {.type = F_FRAME_MEASURE_SPEED_CMD, .size = sizeof(f)} };
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
|
||||
auto r = await_reply<struct speed_frame>(F_FRAME_MEASURE_SPEED_REPLY);
|
||||
return r->period_ms * 1000000;
|
||||
}
|
||||
|
||||
static int large_bulk_transfer(int ep, Bytes& bytes)
|
||||
{
|
||||
if (bytes.size() == 0)
|
||||
return 0;
|
||||
|
||||
int len;
|
||||
int i = libusb_bulk_transfer(device, ep, bytes.begin(), bytes.size(), &len, TIMEOUT);
|
||||
if (i < 0)
|
||||
Error() << fmt::format("data transfer failed at {} bytes: {}", len, usberror(i));
|
||||
return len;
|
||||
}
|
||||
|
||||
void usbTestBulkWrite()
|
||||
{
|
||||
usb_init();
|
||||
|
||||
struct any_frame f = { .f = {.type = F_FRAME_BULK_WRITE_TEST_CMD, .size = sizeof(f)} };
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
|
||||
/* These must match the device. */
|
||||
const int XSIZE = 64;
|
||||
const int YSIZE = 256;
|
||||
const int ZSIZE = 64;
|
||||
|
||||
Bytes bulk_buffer(XSIZE*YSIZE*ZSIZE);
|
||||
double start_time = getCurrentTime();
|
||||
large_bulk_transfer(FLUXENGINE_DATA_IN_EP, bulk_buffer);
|
||||
double elapsed_time = getCurrentTime() - start_time;
|
||||
|
||||
std::cout << "Transferred "
|
||||
<< bulk_buffer.size()
|
||||
<< " bytes from FluxEngine -> PC in "
|
||||
<< int(elapsed_time * 1000.0)
|
||||
<< " ms ("
|
||||
<< int((bulk_buffer.size() / 1024.0) / elapsed_time)
|
||||
<< " kB/s)"
|
||||
<< std::endl;
|
||||
|
||||
for (int x=0; x<XSIZE; x++)
|
||||
for (int y=0; y<YSIZE; y++)
|
||||
for (int z=0; z<ZSIZE; z++)
|
||||
{
|
||||
int offset = x*XSIZE*YSIZE + y*ZSIZE + z;
|
||||
if (bulk_buffer[offset] != uint8_t(x+y+z))
|
||||
Error() << "data transfer corrupted at 0x"
|
||||
<< std::hex << offset << std::dec
|
||||
<< " "
|
||||
<< x << '.' << y << '.' << z << '.';
|
||||
}
|
||||
|
||||
await_reply<struct any_frame>(F_FRAME_BULK_WRITE_TEST_REPLY);
|
||||
}
|
||||
|
||||
void usbTestBulkRead()
|
||||
{
|
||||
usb_init();
|
||||
|
||||
struct any_frame f = { .f = {.type = F_FRAME_BULK_READ_TEST_CMD, .size = sizeof(f)} };
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
|
||||
/* These must match the device. */
|
||||
const int XSIZE = 64;
|
||||
const int YSIZE = 256;
|
||||
const int ZSIZE = 64;
|
||||
|
||||
Bytes bulk_buffer(XSIZE*YSIZE*ZSIZE);
|
||||
for (int x=0; x<XSIZE; x++)
|
||||
for (int y=0; y<YSIZE; y++)
|
||||
for (int z=0; z<ZSIZE; z++)
|
||||
{
|
||||
int offset = x*XSIZE*YSIZE + y*ZSIZE + z;
|
||||
bulk_buffer[offset] = uint8_t(x+y+z);
|
||||
}
|
||||
|
||||
double start_time = getCurrentTime();
|
||||
large_bulk_transfer(FLUXENGINE_DATA_OUT_EP, bulk_buffer);
|
||||
double elapsed_time = getCurrentTime() - start_time;
|
||||
|
||||
std::cout << "Transferred "
|
||||
<< bulk_buffer.size()
|
||||
<< " bytes from PC -> FluxEngine in "
|
||||
<< int(elapsed_time * 1000.0)
|
||||
<< " ms ("
|
||||
<< int((bulk_buffer.size() / 1024.0) / elapsed_time)
|
||||
<< " kB/s)"
|
||||
<< std::endl;
|
||||
|
||||
await_reply<struct any_frame>(F_FRAME_BULK_READ_TEST_REPLY);
|
||||
}
|
||||
|
||||
Bytes usbRead(int side, bool synced, nanoseconds_t readTime)
|
||||
{
|
||||
struct read_frame f = {
|
||||
.f = { .type = F_FRAME_READ_CMD, .size = sizeof(f) },
|
||||
.side = (uint8_t) side,
|
||||
.synced = (uint8_t) synced
|
||||
};
|
||||
uint16_t milliseconds = readTime / 1e6;
|
||||
((uint8_t*)&f.milliseconds)[0] = milliseconds;
|
||||
((uint8_t*)&f.milliseconds)[1] = milliseconds >> 8;
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
|
||||
auto fluxmap = std::unique_ptr<Fluxmap>(new Fluxmap);
|
||||
|
||||
Bytes buffer(1024*1024);
|
||||
int len = large_bulk_transfer(FLUXENGINE_DATA_IN_EP, buffer);
|
||||
buffer.resize(len);
|
||||
|
||||
await_reply<struct any_frame>(F_FRAME_READ_REPLY);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void usbWrite(int side, const Bytes& bytes)
|
||||
{
|
||||
unsigned safelen = bytes.size() & ~(FRAME_SIZE-1);
|
||||
Bytes safeBytes = bytes.slice(0, safelen);
|
||||
|
||||
struct write_frame f = {
|
||||
.f = { .type = F_FRAME_WRITE_CMD, .size = sizeof(f) },
|
||||
.side = (uint8_t) side,
|
||||
};
|
||||
((uint8_t*)&f.bytes_to_write)[0] = safelen;
|
||||
((uint8_t*)&f.bytes_to_write)[1] = safelen >> 8;
|
||||
((uint8_t*)&f.bytes_to_write)[2] = safelen >> 16;
|
||||
((uint8_t*)&f.bytes_to_write)[3] = safelen >> 24;
|
||||
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
large_bulk_transfer(FLUXENGINE_DATA_OUT_EP, safeBytes);
|
||||
|
||||
await_reply<struct any_frame>(F_FRAME_WRITE_REPLY);
|
||||
}
|
||||
|
||||
void usbErase(int side)
|
||||
{
|
||||
struct erase_frame f = {
|
||||
.f = { .type = F_FRAME_ERASE_CMD, .size = sizeof(f) },
|
||||
.side = (uint8_t) side,
|
||||
};
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
|
||||
await_reply<struct any_frame>(F_FRAME_ERASE_REPLY);
|
||||
}
|
||||
|
||||
void usbSetDrive(int drive, bool high_density, int index_mode)
|
||||
{
|
||||
usb_init();
|
||||
|
||||
struct set_drive_frame f = {
|
||||
{ .type = F_FRAME_SET_DRIVE_CMD, .size = sizeof(f) },
|
||||
.drive = (uint8_t) drive,
|
||||
.high_density = high_density,
|
||||
.index_mode = (uint8_t) index_mode
|
||||
};
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
await_reply<struct any_frame>(F_FRAME_SET_DRIVE_REPLY);
|
||||
}
|
||||
|
||||
/* Hacky: the board always operates in little-endian mode. */
|
||||
static uint16_t read_short_from_usb(uint16_t usb)
|
||||
{
|
||||
uint8_t* p = (uint8_t*)&usb;
|
||||
return p[0] | (p[1] << 8);
|
||||
}
|
||||
|
||||
static void convert_voltages_from_usb(const struct voltages& vin, struct voltages& vout)
|
||||
{
|
||||
vout.logic0_mv = read_short_from_usb(vin.logic0_mv);
|
||||
vout.logic1_mv = read_short_from_usb(vin.logic1_mv);
|
||||
}
|
||||
|
||||
void usbMeasureVoltages(struct voltages_frame* voltages)
|
||||
{
|
||||
usb_init();
|
||||
|
||||
struct any_frame f = {
|
||||
{ .type = F_FRAME_MEASURE_VOLTAGES_CMD, .size = sizeof(f) },
|
||||
};
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
|
||||
struct voltages_frame* r = await_reply<struct voltages_frame>(F_FRAME_MEASURE_VOLTAGES_REPLY);
|
||||
convert_voltages_from_usb(r->input_both_off, voltages->input_both_off);
|
||||
convert_voltages_from_usb(r->input_drive_0_selected, voltages->input_drive_0_selected);
|
||||
convert_voltages_from_usb(r->input_drive_1_selected, voltages->input_drive_1_selected);
|
||||
convert_voltages_from_usb(r->input_drive_0_running, voltages->input_drive_0_running);
|
||||
convert_voltages_from_usb(r->input_drive_1_running, voltages->input_drive_1_running);
|
||||
convert_voltages_from_usb(r->output_both_off, voltages->output_both_off);
|
||||
convert_voltages_from_usb(r->output_drive_0_selected, voltages->output_drive_0_selected);
|
||||
convert_voltages_from_usb(r->output_drive_1_selected, voltages->output_drive_1_selected);
|
||||
convert_voltages_from_usb(r->output_drive_0_running, voltages->output_drive_0_running);
|
||||
convert_voltages_from_usb(r->output_drive_1_running, voltages->output_drive_1_running);
|
||||
}
|
||||
19
lib/usb.h
19
lib/usb.h
@@ -1,19 +0,0 @@
|
||||
#ifndef USB_H
|
||||
#define USB_H
|
||||
|
||||
class Fluxmap;
|
||||
class Bytes;
|
||||
|
||||
extern int usbGetVersion();
|
||||
extern void usbRecalibrate();
|
||||
extern void usbSeek(int track);
|
||||
extern nanoseconds_t usbGetRotationalPeriod();
|
||||
extern void usbTestBulkWrite();
|
||||
extern void usbTestBulkRead();
|
||||
extern Bytes usbRead(int side, bool synced, nanoseconds_t readTime);
|
||||
extern void usbWrite(int side, const Bytes& bytes);
|
||||
extern void usbErase(int side);
|
||||
extern void usbSetDrive(int drive, bool high_density, int index_mode);
|
||||
extern void usbMeasureVoltages(struct voltages_frame* voltages);
|
||||
|
||||
#endif
|
||||
326
lib/usb/fluxengineusb.cc
Normal file
326
lib/usb/fluxengineusb.cc
Normal file
@@ -0,0 +1,326 @@
|
||||
#include "globals.h"
|
||||
#include "usb.h"
|
||||
#include "protocol.h"
|
||||
#include "fluxmap.h"
|
||||
#include "bytes.h"
|
||||
#include <libusb.h>
|
||||
#include "fmt/format.h"
|
||||
|
||||
#define TIMEOUT 5000
|
||||
|
||||
/* Hacky: the board always operates in little-endian mode. */
|
||||
static uint16_t read_short_from_usb(uint16_t usb)
|
||||
{
|
||||
uint8_t* p = (uint8_t*)&usb;
|
||||
return p[0] | (p[1] << 8);
|
||||
}
|
||||
|
||||
class FluxEngineUsb : public USB
|
||||
{
|
||||
private:
|
||||
uint8_t _buffer[FRAME_SIZE];
|
||||
|
||||
int usb_cmd_send(void* ptr, int len)
|
||||
{
|
||||
//std::cerr << "send:\n";
|
||||
//hexdump(std::cerr, Bytes((const uint8_t*)ptr, len));
|
||||
int i = libusb_interrupt_transfer(_device, FLUXENGINE_CMD_OUT_EP,
|
||||
(uint8_t*) ptr, len, &len, TIMEOUT);
|
||||
if (i < 0)
|
||||
Error() << "failed to send command: " << usberror(i);
|
||||
return len;
|
||||
}
|
||||
|
||||
void usb_cmd_recv(void* ptr, int len)
|
||||
{
|
||||
int i = libusb_interrupt_transfer(_device, FLUXENGINE_CMD_IN_EP,
|
||||
(uint8_t*) ptr, len, &len, TIMEOUT);
|
||||
if (i < 0)
|
||||
Error() << "failed to receive command reply: " << usberror(i);
|
||||
//std::cerr << "recv:\n";
|
||||
//hexdump(std::cerr, Bytes((const uint8_t*)ptr, len));
|
||||
}
|
||||
|
||||
int large_bulk_transfer(int ep, Bytes& bytes)
|
||||
{
|
||||
if (bytes.size() == 0)
|
||||
return 0;
|
||||
|
||||
int len;
|
||||
int i = libusb_bulk_transfer(_device, ep, bytes.begin(), bytes.size(), &len, TIMEOUT);
|
||||
if (i < 0)
|
||||
Error() << fmt::format("data transfer failed at {} bytes: {}", len, usberror(i));
|
||||
return len;
|
||||
}
|
||||
|
||||
public:
|
||||
FluxEngineUsb(libusb_device_handle* device)
|
||||
{
|
||||
_device = device;
|
||||
|
||||
int i;
|
||||
int cfg = -1;
|
||||
libusb_get_configuration(_device, &cfg);
|
||||
if (cfg != 1)
|
||||
{
|
||||
i = libusb_set_configuration(_device, 1);
|
||||
if (i < 0)
|
||||
Error() << "the FluxEngine would not accept configuration: " << usberror(i);
|
||||
}
|
||||
|
||||
i = libusb_claim_interface(_device, 0);
|
||||
if (i < 0)
|
||||
Error() << "could not claim interface: " << usberror(i);
|
||||
|
||||
int version = getVersion();
|
||||
if (version != FLUXENGINE_VERSION)
|
||||
Error() << "your FluxEngine firmware is at version " << version
|
||||
<< " but the client is for version " << FLUXENGINE_VERSION
|
||||
<< "; please upgrade";
|
||||
}
|
||||
|
||||
private:
|
||||
void bad_reply(void)
|
||||
{
|
||||
struct error_frame* f = (struct error_frame*) _buffer;
|
||||
if (f->f.type != F_FRAME_ERROR)
|
||||
Error() << fmt::format("bad USB reply 0x{:2x}", f->f.type);
|
||||
switch (f->error)
|
||||
{
|
||||
case F_ERROR_BAD_COMMAND:
|
||||
Error() << "device did not understand command";
|
||||
|
||||
case F_ERROR_UNDERRUN:
|
||||
Error() << "USB underrun (not enough bandwidth)";
|
||||
|
||||
default:
|
||||
Error() << fmt::format("unknown device error {}", f->error);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* await_reply(int desired)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
usb_cmd_recv(_buffer, sizeof(_buffer));
|
||||
struct any_frame* r = (struct any_frame*) _buffer;
|
||||
if (r->f.type == F_FRAME_DEBUG)
|
||||
{
|
||||
std::cout << "dev: " << ((struct debug_frame*)r)->payload << std::endl;
|
||||
continue;
|
||||
}
|
||||
if (r->f.type != desired)
|
||||
bad_reply();
|
||||
return (T*) r;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
int getVersion()
|
||||
{
|
||||
struct any_frame f = { .f = {.type = F_FRAME_GET_VERSION_CMD, .size = sizeof(f)} };
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
auto r = await_reply<struct version_frame>(F_FRAME_GET_VERSION_REPLY);
|
||||
return r->version;
|
||||
}
|
||||
|
||||
void seek(int track)
|
||||
{
|
||||
struct seek_frame f = {
|
||||
{ .type = F_FRAME_SEEK_CMD, .size = sizeof(f) },
|
||||
.track = (uint8_t) track
|
||||
};
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
await_reply<struct any_frame>(F_FRAME_SEEK_REPLY);
|
||||
}
|
||||
|
||||
void recalibrate()
|
||||
{
|
||||
struct any_frame f = {
|
||||
{ .type = F_FRAME_RECALIBRATE_CMD, .size = sizeof(f) },
|
||||
};
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
await_reply<struct any_frame>(F_FRAME_RECALIBRATE_REPLY);
|
||||
}
|
||||
|
||||
nanoseconds_t getRotationalPeriod(void)
|
||||
{
|
||||
struct any_frame f = { .f = {.type = F_FRAME_MEASURE_SPEED_CMD, .size = sizeof(f)} };
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
|
||||
auto r = await_reply<struct speed_frame>(F_FRAME_MEASURE_SPEED_REPLY);
|
||||
return r->period_ms * 1000000;
|
||||
}
|
||||
|
||||
void testBulkWrite()
|
||||
{
|
||||
struct any_frame f = { .f = {.type = F_FRAME_BULK_WRITE_TEST_CMD, .size = sizeof(f)} };
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
|
||||
/* These must match the device. */
|
||||
const int XSIZE = 64;
|
||||
const int YSIZE = 256;
|
||||
const int ZSIZE = 64;
|
||||
|
||||
Bytes bulk_buffer(XSIZE*YSIZE*ZSIZE);
|
||||
double start_time = getCurrentTime();
|
||||
large_bulk_transfer(FLUXENGINE_DATA_IN_EP, bulk_buffer);
|
||||
double elapsed_time = getCurrentTime() - start_time;
|
||||
|
||||
std::cout << "Transferred "
|
||||
<< bulk_buffer.size()
|
||||
<< " bytes from FluxEngine -> PC in "
|
||||
<< int(elapsed_time * 1000.0)
|
||||
<< " ms ("
|
||||
<< int((bulk_buffer.size() / 1024.0) / elapsed_time)
|
||||
<< " kB/s)"
|
||||
<< std::endl;
|
||||
|
||||
for (int x=0; x<XSIZE; x++)
|
||||
for (int y=0; y<YSIZE; y++)
|
||||
for (int z=0; z<ZSIZE; z++)
|
||||
{
|
||||
int offset = x*XSIZE*YSIZE + y*ZSIZE + z;
|
||||
if (bulk_buffer[offset] != uint8_t(x+y+z))
|
||||
Error() << "data transfer corrupted at 0x"
|
||||
<< std::hex << offset << std::dec
|
||||
<< " "
|
||||
<< x << '.' << y << '.' << z << '.';
|
||||
}
|
||||
|
||||
await_reply<struct any_frame>(F_FRAME_BULK_WRITE_TEST_REPLY);
|
||||
}
|
||||
|
||||
void testBulkRead()
|
||||
{
|
||||
struct any_frame f = { .f = {.type = F_FRAME_BULK_READ_TEST_CMD, .size = sizeof(f)} };
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
|
||||
/* These must match the device. */
|
||||
const int XSIZE = 64;
|
||||
const int YSIZE = 256;
|
||||
const int ZSIZE = 64;
|
||||
|
||||
Bytes bulk_buffer(XSIZE*YSIZE*ZSIZE);
|
||||
for (int x=0; x<XSIZE; x++)
|
||||
for (int y=0; y<YSIZE; y++)
|
||||
for (int z=0; z<ZSIZE; z++)
|
||||
{
|
||||
int offset = x*XSIZE*YSIZE + y*ZSIZE + z;
|
||||
bulk_buffer[offset] = uint8_t(x+y+z);
|
||||
}
|
||||
|
||||
double start_time = getCurrentTime();
|
||||
large_bulk_transfer(FLUXENGINE_DATA_OUT_EP, bulk_buffer);
|
||||
double elapsed_time = getCurrentTime() - start_time;
|
||||
|
||||
std::cout << "Transferred "
|
||||
<< bulk_buffer.size()
|
||||
<< " bytes from PC -> FluxEngine in "
|
||||
<< int(elapsed_time * 1000.0)
|
||||
<< " ms ("
|
||||
<< int((bulk_buffer.size() / 1024.0) / elapsed_time)
|
||||
<< " kB/s)"
|
||||
<< std::endl;
|
||||
|
||||
await_reply<struct any_frame>(F_FRAME_BULK_READ_TEST_REPLY);
|
||||
}
|
||||
|
||||
Bytes read(int side, bool synced, nanoseconds_t readTime)
|
||||
{
|
||||
struct read_frame f = {
|
||||
.f = { .type = F_FRAME_READ_CMD, .size = sizeof(f) },
|
||||
.side = (uint8_t) side,
|
||||
.synced = (uint8_t) synced
|
||||
};
|
||||
uint16_t milliseconds = readTime / 1e6;
|
||||
((uint8_t*)&f.milliseconds)[0] = milliseconds;
|
||||
((uint8_t*)&f.milliseconds)[1] = milliseconds >> 8;
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
|
||||
auto fluxmap = std::unique_ptr<Fluxmap>(new Fluxmap);
|
||||
|
||||
Bytes buffer(1024*1024);
|
||||
int len = large_bulk_transfer(FLUXENGINE_DATA_IN_EP, buffer);
|
||||
buffer.resize(len);
|
||||
|
||||
await_reply<struct any_frame>(F_FRAME_READ_REPLY);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void write(int side, const Bytes& bytes)
|
||||
{
|
||||
unsigned safelen = bytes.size() & ~(FRAME_SIZE-1);
|
||||
Bytes safeBytes = bytes.slice(0, safelen);
|
||||
|
||||
struct write_frame f = {
|
||||
.f = { .type = F_FRAME_WRITE_CMD, .size = sizeof(f) },
|
||||
.side = (uint8_t) side,
|
||||
};
|
||||
((uint8_t*)&f.bytes_to_write)[0] = safelen;
|
||||
((uint8_t*)&f.bytes_to_write)[1] = safelen >> 8;
|
||||
((uint8_t*)&f.bytes_to_write)[2] = safelen >> 16;
|
||||
((uint8_t*)&f.bytes_to_write)[3] = safelen >> 24;
|
||||
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
large_bulk_transfer(FLUXENGINE_DATA_OUT_EP, safeBytes);
|
||||
|
||||
await_reply<struct any_frame>(F_FRAME_WRITE_REPLY);
|
||||
}
|
||||
|
||||
void erase(int side)
|
||||
{
|
||||
struct erase_frame f = {
|
||||
.f = { .type = F_FRAME_ERASE_CMD, .size = sizeof(f) },
|
||||
.side = (uint8_t) side,
|
||||
};
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
|
||||
await_reply<struct any_frame>(F_FRAME_ERASE_REPLY);
|
||||
}
|
||||
|
||||
void setDrive(int drive, bool high_density, int index_mode)
|
||||
{
|
||||
struct set_drive_frame f = {
|
||||
{ .type = F_FRAME_SET_DRIVE_CMD, .size = sizeof(f) },
|
||||
.drive = (uint8_t) drive,
|
||||
.high_density = high_density,
|
||||
.index_mode = (uint8_t) index_mode
|
||||
};
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
await_reply<struct any_frame>(F_FRAME_SET_DRIVE_REPLY);
|
||||
}
|
||||
|
||||
void measureVoltages(struct voltages_frame* voltages)
|
||||
{
|
||||
struct any_frame f = {
|
||||
{ .type = F_FRAME_MEASURE_VOLTAGES_CMD, .size = sizeof(f) },
|
||||
};
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
|
||||
auto convert_voltages_from_usb = [&](const struct voltages& vin, struct voltages& vout)
|
||||
{
|
||||
vout.logic0_mv = read_short_from_usb(vin.logic0_mv);
|
||||
vout.logic1_mv = read_short_from_usb(vin.logic1_mv);
|
||||
};
|
||||
|
||||
struct voltages_frame* r = await_reply<struct voltages_frame>(F_FRAME_MEASURE_VOLTAGES_REPLY);
|
||||
convert_voltages_from_usb(r->input_both_off, voltages->input_both_off);
|
||||
convert_voltages_from_usb(r->input_drive_0_selected, voltages->input_drive_0_selected);
|
||||
convert_voltages_from_usb(r->input_drive_1_selected, voltages->input_drive_1_selected);
|
||||
convert_voltages_from_usb(r->input_drive_0_running, voltages->input_drive_0_running);
|
||||
convert_voltages_from_usb(r->input_drive_1_running, voltages->input_drive_1_running);
|
||||
convert_voltages_from_usb(r->output_both_off, voltages->output_both_off);
|
||||
convert_voltages_from_usb(r->output_drive_0_selected, voltages->output_drive_0_selected);
|
||||
convert_voltages_from_usb(r->output_drive_1_selected, voltages->output_drive_1_selected);
|
||||
convert_voltages_from_usb(r->output_drive_0_running, voltages->output_drive_0_running);
|
||||
convert_voltages_from_usb(r->output_drive_1_running, voltages->output_drive_1_running);
|
||||
}
|
||||
};
|
||||
|
||||
USB* createFluxengineUsb(libusb_device_handle* device)
|
||||
{
|
||||
return new FluxEngineUsb(device);
|
||||
}
|
||||
|
||||
139
lib/usb/usb.cc
Normal file
139
lib/usb/usb.cc
Normal file
@@ -0,0 +1,139 @@
|
||||
#include "globals.h"
|
||||
#include "flags.h"
|
||||
#include "usb.h"
|
||||
#include "protocol.h"
|
||||
#include "fluxmap.h"
|
||||
#include "bytes.h"
|
||||
#include <libusb.h>
|
||||
#include "fmt/format.h"
|
||||
|
||||
FlagGroup usbFlags;
|
||||
|
||||
static StringFlag device(
|
||||
{ "--device" },
|
||||
"serial number of hardware device to use",
|
||||
"");
|
||||
|
||||
static USB* usb = NULL;
|
||||
|
||||
enum
|
||||
{
|
||||
DEV_FLUXENGINE,
|
||||
};
|
||||
|
||||
struct CandidateDevice
|
||||
{
|
||||
libusb_device* device;
|
||||
libusb_device_descriptor desc;
|
||||
int type;
|
||||
std::string serial;
|
||||
};
|
||||
|
||||
USB::~USB()
|
||||
{}
|
||||
|
||||
std::string USB::usberror(int i)
|
||||
{
|
||||
return libusb_strerror((libusb_error) i);
|
||||
}
|
||||
|
||||
static const char* device_type(int i)
|
||||
{
|
||||
switch (i)
|
||||
{
|
||||
case DEV_FLUXENGINE: return "FluxEngine";
|
||||
default: assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
static std::map<std::string, std::unique_ptr<CandidateDevice>> get_candidates(libusb_device** devices, int numdevices)
|
||||
{
|
||||
std::map<std::string, std::unique_ptr<CandidateDevice>> candidates;
|
||||
for (int i=0; i<numdevices; i++)
|
||||
{
|
||||
std::unique_ptr<CandidateDevice> candidate(new CandidateDevice());
|
||||
candidate->device = devices[i];
|
||||
(void) libusb_get_device_descriptor(devices[i], &candidate->desc);
|
||||
|
||||
if ((candidate->desc.idVendor == FLUXENGINE_VID) && (candidate->desc.idProduct == FLUXENGINE_PID))
|
||||
{
|
||||
candidate->type = DEV_FLUXENGINE;
|
||||
candidate->serial = "";
|
||||
|
||||
libusb_device_handle* handle;
|
||||
if (libusb_open(candidate->device, &handle) == 0)
|
||||
{
|
||||
unsigned char buffer[64];
|
||||
libusb_get_string_descriptor_ascii(handle,
|
||||
candidate->desc.iSerialNumber, buffer, sizeof(buffer));
|
||||
candidate->serial = (const char*) buffer;
|
||||
libusb_close(handle);
|
||||
}
|
||||
|
||||
candidates[candidate->serial] = std::move(candidate);
|
||||
}
|
||||
}
|
||||
|
||||
return candidates;
|
||||
}
|
||||
|
||||
static void open_device(CandidateDevice& candidate)
|
||||
{
|
||||
libusb_device_handle* handle;
|
||||
int i = libusb_open(candidate.device, &handle);
|
||||
if (i < 0)
|
||||
Error() << "cannot open USB device: " << libusb_strerror((libusb_error) i);
|
||||
|
||||
std::cout << "Using " << device_type(candidate.type) << " with serial number " << candidate.serial << '\n';
|
||||
usb = createFluxengineUsb(handle);
|
||||
}
|
||||
|
||||
static CandidateDevice& select_candidate(const std::map<std::string, std::unique_ptr<CandidateDevice>>& devices)
|
||||
{
|
||||
if (devices.size() == 0)
|
||||
Error() << "no USB devices found (is one plugged in? Do you have permission to access USB devices?)";
|
||||
|
||||
if (device.get() == "")
|
||||
{
|
||||
if (devices.size() == 1)
|
||||
return *devices.begin()->second;
|
||||
|
||||
std::cout << "More than one USB device detected. Use --device to specify which one to use:\n";
|
||||
for (auto& i : devices)
|
||||
std::cout << " " << device_type(i.second->type) << ": " << i.first << '\n';
|
||||
Error() << "specify USB device";
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto& i = devices.find(device);
|
||||
if (i != devices.end())
|
||||
return *i->second;
|
||||
|
||||
Error() << "device with serial number '" << device.get() << "' not found";
|
||||
}
|
||||
}
|
||||
|
||||
USB& getUsb()
|
||||
{
|
||||
if (!usb)
|
||||
{
|
||||
int i = libusb_init(NULL);
|
||||
if (i < 0)
|
||||
Error() << "could not start libusb: " << libusb_strerror((libusb_error) i);
|
||||
|
||||
libusb_device** devices;
|
||||
int numdevices = libusb_get_device_list(NULL, &devices);
|
||||
if (numdevices < 0)
|
||||
Error() << "could not enumerate USB bus: " << libusb_strerror((libusb_error) numdevices);
|
||||
|
||||
auto candidates = get_candidates(devices, numdevices);
|
||||
auto candidate = select_candidate(candidates);
|
||||
open_device(candidate);
|
||||
|
||||
libusb_free_device_list(devices, true);
|
||||
|
||||
}
|
||||
|
||||
return *usb;
|
||||
}
|
||||
|
||||
60
lib/usb/usb.h
Normal file
60
lib/usb/usb.h
Normal file
@@ -0,0 +1,60 @@
|
||||
#ifndef USB_H
|
||||
#define USB_H
|
||||
|
||||
#include "bytes.h"
|
||||
#include "flags.h"
|
||||
|
||||
class Fluxmap;
|
||||
class libusb_device_handle;
|
||||
|
||||
class USB
|
||||
{
|
||||
public:
|
||||
virtual ~USB();
|
||||
|
||||
virtual int getVersion() = 0;
|
||||
virtual void recalibrate() = 0;
|
||||
virtual void seek(int track) = 0;
|
||||
virtual nanoseconds_t getRotationalPeriod() = 0;
|
||||
virtual void testBulkWrite() = 0;
|
||||
virtual void testBulkRead() = 0;
|
||||
virtual Bytes read(int side, bool synced, nanoseconds_t readTime) = 0;
|
||||
virtual void write(int side, const Bytes& bytes) = 0;
|
||||
virtual void erase(int side) = 0;
|
||||
virtual void setDrive(int drive, bool high_density, int index_mode) = 0;
|
||||
virtual void measureVoltages(struct voltages_frame* voltages) = 0;
|
||||
|
||||
protected:
|
||||
std::string usberror(int i);
|
||||
|
||||
libusb_device_handle* _device;
|
||||
};
|
||||
|
||||
extern FlagGroup usbFlags;
|
||||
extern USB& getUsb();
|
||||
|
||||
extern USB* createFluxengineUsb(libusb_device_handle* device);
|
||||
|
||||
static inline int usbGetVersion() { return getUsb().getVersion(); }
|
||||
static inline void usbRecalibrate() { getUsb().recalibrate(); }
|
||||
static inline void usbSeek(int track) { getUsb().seek(track); }
|
||||
static inline void usbTestBulkWrite() { getUsb().testBulkWrite(); }
|
||||
static inline void usbTestBulkRead() { getUsb().testBulkRead(); }
|
||||
static inline void usbErase(int side) { getUsb().erase(side); }
|
||||
|
||||
static inline nanoseconds_t usbGetRotationalPeriod()
|
||||
{ return getUsb().getRotationalPeriod(); }
|
||||
|
||||
static inline Bytes usbRead(int side, bool synced, nanoseconds_t readTime)
|
||||
{ return getUsb().read(side, synced, readTime); }
|
||||
|
||||
static inline void usbWrite(int side, const Bytes& bytes)
|
||||
{ getUsb().write(side, bytes); }
|
||||
|
||||
static inline void usbSetDrive(int drive, bool high_density, int index_mode)
|
||||
{ getUsb().setDrive(drive, high_density, index_mode); }
|
||||
|
||||
static inline void usbMeasureVoltages(struct voltages_frame* voltages)
|
||||
{ getUsb().measureVoltages(voltages); }
|
||||
|
||||
#endif
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "writer.h"
|
||||
#include "sql.h"
|
||||
#include "protocol.h"
|
||||
#include "usb.h"
|
||||
#include "usb/usb.h"
|
||||
#include "dataspec.h"
|
||||
#include "encoders/encoders.h"
|
||||
#include "fluxsource/fluxsource.h"
|
||||
|
||||
@@ -191,6 +191,8 @@ buildlibrary libbackend.a \
|
||||
lib/fluxsource/kryoflux.cc \
|
||||
lib/fluxsource/sqlitefluxsource.cc \
|
||||
lib/fluxsource/streamfluxsource.cc \
|
||||
lib/usb/usb.cc \
|
||||
lib/usb/fluxengineusb.cc \
|
||||
lib/globals.cc \
|
||||
lib/hexdump.cc \
|
||||
lib/ldbs.cc \
|
||||
@@ -198,7 +200,6 @@ buildlibrary libbackend.a \
|
||||
lib/sector.cc \
|
||||
lib/sectorset.cc \
|
||||
lib/sql.cc \
|
||||
lib/usb.cc \
|
||||
lib/visualiser.cc \
|
||||
lib/writer.cc \
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
#include "globals.h"
|
||||
#include "flags.h"
|
||||
#include "usb.h"
|
||||
#include "usb/usb.h"
|
||||
#include "dataspec.h"
|
||||
#include "protocol.h"
|
||||
|
||||
static FlagGroup flags;
|
||||
static FlagGroup flags = {
|
||||
&usbFlags,
|
||||
};
|
||||
|
||||
static DataSpecFlag source(
|
||||
{ "--source", "-s" },
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
#include "globals.h"
|
||||
#include "flags.h"
|
||||
#include "usb.h"
|
||||
#include "usb/usb.h"
|
||||
#include "protocol.h"
|
||||
|
||||
static FlagGroup flags;
|
||||
static FlagGroup flags = {
|
||||
&usbFlags,
|
||||
};
|
||||
|
||||
static IntFlag drive(
|
||||
{ "--drive", "-d" },
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#include "globals.h"
|
||||
#include "flags.h"
|
||||
#include "usb.h"
|
||||
#include "usb/usb.h"
|
||||
|
||||
static FlagGroup flags;
|
||||
static FlagGroup flags = {
|
||||
&usbFlags,
|
||||
};
|
||||
|
||||
int mainTestBandwidth(int argc, const char* argv[])
|
||||
{
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
#include "globals.h"
|
||||
#include "flags.h"
|
||||
#include "usb.h"
|
||||
#include "usb/usb.h"
|
||||
#include "protocol.h"
|
||||
#include <fmt/format.h>
|
||||
|
||||
static FlagGroup flags;
|
||||
static FlagGroup flags = {
|
||||
&usbFlags,
|
||||
};
|
||||
|
||||
static std::string display_voltages(struct voltages& v)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user