diff --git a/doc/using.md b/doc/using.md index 1adafe23..f82bfd44 100644 --- a/doc/using.md +++ b/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 diff --git a/lib/fluxsink/hardwarefluxsink.cc b/lib/fluxsink/hardwarefluxsink.cc index 11715502..93d5eb54 100644 --- a/lib/fluxsink/hardwarefluxsink.cc +++ b/lib/fluxsink/hardwarefluxsink.cc @@ -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; diff --git a/lib/fluxsource/hardwarefluxsource.cc b/lib/fluxsource/hardwarefluxsource.cc index 82c21800..621fbd8b 100644 --- a/lib/fluxsource/hardwarefluxsource.cc +++ b/lib/fluxsource/hardwarefluxsource.cc @@ -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" }, diff --git a/lib/reader.cc b/lib/reader.cc index 6e20169a..96b404d8 100644 --- a/lib/reader.cc +++ b/lib/reader.cc @@ -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" diff --git a/lib/usb.cc b/lib/usb.cc deleted file mode 100644 index 583b13fa..00000000 --- a/lib/usb.cc +++ /dev/null @@ -1,344 +0,0 @@ -#include "globals.h" -#include "usb.h" -#include "protocol.h" -#include "fluxmap.h" -#include "bytes.h" -#include -#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 -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(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(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(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(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(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 FluxEngine in " - << int(elapsed_time * 1000.0) - << " ms (" - << int((bulk_buffer.size() / 1024.0) / elapsed_time) - << " kB/s)" - << std::endl; - - await_reply(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(new Fluxmap); - - Bytes buffer(1024*1024); - int len = large_bulk_transfer(FLUXENGINE_DATA_IN_EP, buffer); - buffer.resize(len); - - await_reply(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(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(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(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(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); -} diff --git a/lib/usb.h b/lib/usb.h deleted file mode 100644 index 043f5f83..00000000 --- a/lib/usb.h +++ /dev/null @@ -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 diff --git a/lib/usb/fluxengineusb.cc b/lib/usb/fluxengineusb.cc new file mode 100644 index 00000000..1a93b6c2 --- /dev/null +++ b/lib/usb/fluxengineusb.cc @@ -0,0 +1,326 @@ +#include "globals.h" +#include "usb.h" +#include "protocol.h" +#include "fluxmap.h" +#include "bytes.h" +#include +#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 + 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(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(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(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(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(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 FluxEngine in " + << int(elapsed_time * 1000.0) + << " ms (" + << int((bulk_buffer.size() / 1024.0) / elapsed_time) + << " kB/s)" + << std::endl; + + await_reply(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(new Fluxmap); + + Bytes buffer(1024*1024); + int len = large_bulk_transfer(FLUXENGINE_DATA_IN_EP, buffer); + buffer.resize(len); + + await_reply(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(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(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(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(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); +} + diff --git a/lib/usb/usb.cc b/lib/usb/usb.cc new file mode 100644 index 00000000..866068af --- /dev/null +++ b/lib/usb/usb.cc @@ -0,0 +1,139 @@ +#include "globals.h" +#include "flags.h" +#include "usb.h" +#include "protocol.h" +#include "fluxmap.h" +#include "bytes.h" +#include +#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> get_candidates(libusb_device** devices, int numdevices) +{ + std::map> candidates; + for (int i=0; i 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>& 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; +} + diff --git a/lib/usb/usb.h b/lib/usb/usb.h new file mode 100644 index 00000000..e20b043c --- /dev/null +++ b/lib/usb/usb.h @@ -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 diff --git a/lib/writer.cc b/lib/writer.cc index f079cfd5..2bd4ad82 100644 --- a/lib/writer.cc +++ b/lib/writer.cc @@ -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" diff --git a/mkninja.sh b/mkninja.sh index f4e02b44..a40d1588 100644 --- a/mkninja.sh +++ b/mkninja.sh @@ -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 \ diff --git a/src/fe-rpm.cc b/src/fe-rpm.cc index f58513fa..69e09933 100644 --- a/src/fe-rpm.cc +++ b/src/fe-rpm.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" }, diff --git a/src/fe-seek.cc b/src/fe-seek.cc index 5cdadb5f..0cb80eeb 100644 --- a/src/fe-seek.cc +++ b/src/fe-seek.cc @@ -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" }, diff --git a/src/fe-testbandwidth.cc b/src/fe-testbandwidth.cc index 6603cd00..b45a9a68 100644 --- a/src/fe-testbandwidth.cc +++ b/src/fe-testbandwidth.cc @@ -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[]) { diff --git a/src/fe-testvoltages.cc b/src/fe-testvoltages.cc index b1c24377..61cb625b 100644 --- a/src/fe-testvoltages.cc +++ b/src/fe-testvoltages.cc @@ -1,10 +1,12 @@ #include "globals.h" #include "flags.h" -#include "usb.h" +#include "usb/usb.h" #include "protocol.h" #include -static FlagGroup flags; +static FlagGroup flags = { + &usbFlags, +}; static std::string display_voltages(struct voltages& v) {