From 4fee29307cf492e4bdcd17b310460990df7b1924 Mon Sep 17 00:00:00 2001 From: David Given Date: Sat, 8 Aug 2020 14:52:11 +0100 Subject: [PATCH 1/6] Refactor the USB stuff to allow for multiple USB implementations. --- lib/usb.cc | 566 ++++++++++++++++++++++++++--------------------------- lib/usb.h | 57 ++++-- 2 files changed, 327 insertions(+), 296 deletions(-) diff --git a/lib/usb.cc b/lib/usb.cc index 583b13fa..5dce568b 100644 --- a/lib/usb.cc +++ b/lib/usb.cc @@ -9,6 +9,7 @@ #define TIMEOUT 5000 static libusb_device_handle* device; +static USB* usb = NULL; static uint8_t buffer[FRAME_SIZE]; @@ -17,39 +18,6 @@ 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"; @@ -61,7 +29,7 @@ static int usb_cmd_send(void* ptr, int len) return len; } -void usb_cmd_recv(void* ptr, int len) +static void usb_cmd_recv(void* ptr, int len) { int i = libusb_interrupt_transfer(device, FLUXENGINE_CMD_IN_EP, (uint8_t*) ptr, len, &len, TIMEOUT); @@ -71,86 +39,6 @@ void usb_cmd_recv(void* ptr, int len) //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) @@ -163,182 +51,294 @@ static int large_bulk_transfer(int ep, Bytes& bytes) 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); + 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) +USB::~USB() +{} + +class FluxEngineUsb : public USB { - vout.logic0_mv = read_short_from_usb(vin.logic0_mv); - vout.logic1_mv = read_short_from_usb(vin.logic1_mv); -} +public: + FluxEngineUsb() + { + int i = libusb_init(NULL); + if (i < 0) + Error() << "could not start libusb: " << usberror(i); -void usbMeasureVoltages(struct voltages_frame* voltages) + 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"; + } + +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& getUsb() { - usb_init(); + if (!usb) + usb = new FluxEngineUsb(); - 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); + return *usb; } + diff --git a/lib/usb.h b/lib/usb.h index 043f5f83..f40cda3a 100644 --- a/lib/usb.h +++ b/lib/usb.h @@ -1,19 +1,50 @@ #ifndef USB_H #define USB_H -class Fluxmap; -class Bytes; +#include "bytes.h" -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); +class Fluxmap; + +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; +}; + +extern USB& getUsb(); + +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 From 8b6be5a5016c629742891a8a9533773ac5f2abd1 Mon Sep 17 00:00:00 2001 From: David Given Date: Sun, 9 Aug 2020 21:14:09 +0200 Subject: [PATCH 2/6] Move usb.{cc,h} into its own directory. --- lib/fluxsink/hardwarefluxsink.cc | 2 +- lib/fluxsource/hardwarefluxsource.cc | 2 +- lib/reader.cc | 2 +- lib/{ => usb}/usb.cc | 0 lib/{ => usb}/usb.h | 0 lib/writer.cc | 2 +- mkninja.sh | 2 +- src/fe-rpm.cc | 2 +- src/fe-seek.cc | 2 +- src/fe-testbandwidth.cc | 2 +- src/fe-testvoltages.cc | 2 +- 11 files changed, 9 insertions(+), 9 deletions(-) rename lib/{ => usb}/usb.cc (100%) rename lib/{ => usb}/usb.h (100%) diff --git a/lib/fluxsink/hardwarefluxsink.cc b/lib/fluxsink/hardwarefluxsink.cc index 11715502..d5d211a9 100644 --- a/lib/fluxsink/hardwarefluxsink.cc +++ b/lib/fluxsink/hardwarefluxsink.cc @@ -1,7 +1,7 @@ #include "globals.h" #include "flags.h" #include "fluxmap.h" -#include "usb.h" +#include "usb/usb.h" #include "fluxsink/fluxsink.h" FlagGroup hardwareFluxSinkFlags; diff --git a/lib/fluxsource/hardwarefluxsource.cc b/lib/fluxsource/hardwarefluxsource.cc index 82c21800..8e5508b1 100644 --- a/lib/fluxsource/hardwarefluxsource.cc +++ b/lib/fluxsource/hardwarefluxsource.cc @@ -1,7 +1,7 @@ #include "globals.h" #include "flags.h" #include "fluxmap.h" -#include "usb.h" +#include "usb/usb.h" #include "fluxsource/fluxsource.h" #include "fmt/format.h" 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/usb.cc similarity index 100% rename from lib/usb.cc rename to lib/usb/usb.cc diff --git a/lib/usb.h b/lib/usb/usb.h similarity index 100% rename from lib/usb.h rename to lib/usb/usb.h 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 e6c1e1b2..d42e271c 100644 --- a/mkninja.sh +++ b/mkninja.sh @@ -190,6 +190,7 @@ buildlibrary libbackend.a \ lib/fluxsource/kryoflux.cc \ lib/fluxsource/sqlitefluxsource.cc \ lib/fluxsource/streamfluxsource.cc \ + lib/usb/usb.cc \ lib/globals.cc \ lib/hexdump.cc \ lib/ldbs.cc \ @@ -197,7 +198,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..fe3d55d3 100644 --- a/src/fe-rpm.cc +++ b/src/fe-rpm.cc @@ -1,6 +1,6 @@ #include "globals.h" #include "flags.h" -#include "usb.h" +#include "usb/usb.h" #include "dataspec.h" #include "protocol.h" diff --git a/src/fe-seek.cc b/src/fe-seek.cc index 5cdadb5f..3591fba3 100644 --- a/src/fe-seek.cc +++ b/src/fe-seek.cc @@ -1,6 +1,6 @@ #include "globals.h" #include "flags.h" -#include "usb.h" +#include "usb/usb.h" #include "protocol.h" static FlagGroup flags; diff --git a/src/fe-testbandwidth.cc b/src/fe-testbandwidth.cc index 6603cd00..08e6beca 100644 --- a/src/fe-testbandwidth.cc +++ b/src/fe-testbandwidth.cc @@ -1,6 +1,6 @@ #include "globals.h" #include "flags.h" -#include "usb.h" +#include "usb/usb.h" static FlagGroup flags; diff --git a/src/fe-testvoltages.cc b/src/fe-testvoltages.cc index b1c24377..f8d6c477 100644 --- a/src/fe-testvoltages.cc +++ b/src/fe-testvoltages.cc @@ -1,6 +1,6 @@ #include "globals.h" #include "flags.h" -#include "usb.h" +#include "usb/usb.h" #include "protocol.h" #include From 2727e66d402a7efe416ce48baa7932a493eea797 Mon Sep 17 00:00:00 2001 From: David Given Date: Sun, 9 Aug 2020 22:33:54 +0200 Subject: [PATCH 3/6] Allow multiple USB implementations. --- lib/usb/fluxengineusb.cc | 331 +++++++++++++++++++++++++++++++++++++++ lib/usb/usb.cc | 326 +------------------------------------- lib/usb/usb.h | 8 + mkninja.sh | 1 + 4 files changed, 344 insertions(+), 322 deletions(-) create mode 100644 lib/usb/fluxengineusb.cc diff --git a/lib/usb/fluxengineusb.cc b/lib/usb/fluxengineusb.cc new file mode 100644 index 00000000..a0f9c517 --- /dev/null +++ b/lib/usb/fluxengineusb.cc @@ -0,0 +1,331 @@ +#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() + { + 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"; + } + +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() +{ + return new FluxEngineUsb(); +} + diff --git a/lib/usb/usb.cc b/lib/usb/usb.cc index 5dce568b..acf29d8b 100644 --- a/lib/usb/usb.cc +++ b/lib/usb/usb.cc @@ -6,338 +6,20 @@ #include #include "fmt/format.h" -#define TIMEOUT 5000 - -static libusb_device_handle* device; static USB* usb = NULL; -static uint8_t buffer[FRAME_SIZE]; - -static std::string usberror(int i) -{ - return libusb_strerror((libusb_error) i); -} - -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; -} - -static 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 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; -} - -/* 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); -} - USB::~USB() {} -class FluxEngineUsb : public USB +std::string USB::usberror(int i) { -public: - FluxEngineUsb() - { - 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"; - } - -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); - } -}; + return libusb_strerror((libusb_error) i); +} USB& getUsb() { if (!usb) - usb = new FluxEngineUsb(); + usb = createFluxengineUsb(); return *usb; } diff --git a/lib/usb/usb.h b/lib/usb/usb.h index f40cda3a..292c0c1c 100644 --- a/lib/usb/usb.h +++ b/lib/usb/usb.h @@ -4,6 +4,7 @@ #include "bytes.h" class Fluxmap; +class libusb_device_handle; class USB { @@ -21,10 +22,17 @@ public: 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 USB& getUsb(); +extern USB* createFluxengineUsb(); + static inline int usbGetVersion() { return getUsb().getVersion(); } static inline void usbRecalibrate() { getUsb().recalibrate(); } static inline void usbSeek(int track) { getUsb().seek(track); } diff --git a/mkninja.sh b/mkninja.sh index d42e271c..a50cfe50 100644 --- a/mkninja.sh +++ b/mkninja.sh @@ -191,6 +191,7 @@ buildlibrary libbackend.a \ 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 \ From 73398b83a9d84e32b9e9964bb285e648cc04b5d3 Mon Sep 17 00:00:00 2001 From: David Given Date: Mon, 10 Aug 2020 22:36:47 +0200 Subject: [PATCH 4/6] Add support for specifying which FluxEngine you want to use with the --devices parameter. --- lib/fluxsink/hardwarefluxsink.cc | 4 +- lib/fluxsource/hardwarefluxsource.cc | 4 +- lib/usb/fluxengineusb.cc | 17 ++-- lib/usb/usb.cc | 112 ++++++++++++++++++++++++++- lib/usb/usb.h | 4 +- src/fe-rpm.cc | 4 +- src/fe-seek.cc | 4 +- src/fe-testbandwidth.cc | 4 +- src/fe-testvoltages.cc | 4 +- 9 files changed, 138 insertions(+), 19 deletions(-) diff --git a/lib/fluxsink/hardwarefluxsink.cc b/lib/fluxsink/hardwarefluxsink.cc index d5d211a9..93d5eb54 100644 --- a/lib/fluxsink/hardwarefluxsink.cc +++ b/lib/fluxsink/hardwarefluxsink.cc @@ -4,7 +4,9 @@ #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 8e5508b1..621fbd8b 100644 --- a/lib/fluxsource/hardwarefluxsource.cc +++ b/lib/fluxsource/hardwarefluxsource.cc @@ -5,7 +5,9 @@ #include "fluxsource/fluxsource.h" #include "fmt/format.h" -FlagGroup hardwareFluxSourceFlags; +FlagGroup hardwareFluxSourceFlags = { + &usbFlags +}; static DoubleFlag revolutions( { "--revolutions" }, diff --git a/lib/usb/fluxengineusb.cc b/lib/usb/fluxengineusb.cc index a0f9c517..1a93b6c2 100644 --- a/lib/usb/fluxengineusb.cc +++ b/lib/usb/fluxengineusb.cc @@ -54,16 +54,11 @@ private: } public: - FluxEngineUsb() + FluxEngineUsb(libusb_device_handle* device) { - int i = libusb_init(NULL); - if (i < 0) - Error() << "could not start libusb: " << usberror(i); + _device = device; - _device = libusb_open_device_with_vid_pid(NULL, FLUXENGINE_VID, FLUXENGINE_PID); - if (!_device) - Error() << "cannot find the FluxEngine (is it plugged in?)"; - + int i; int cfg = -1; libusb_get_configuration(_device, &cfg); if (cfg != 1) @@ -77,7 +72,7 @@ public: if (i < 0) Error() << "could not claim interface: " << usberror(i); - int version = usbGetVersion(); + int version = getVersion(); if (version != FLUXENGINE_VERSION) Error() << "your FluxEngine firmware is at version " << version << " but the client is for version " << FLUXENGINE_VERSION @@ -324,8 +319,8 @@ public: } }; -USB* createFluxengineUsb() +USB* createFluxengineUsb(libusb_device_handle* device) { - return new FluxEngineUsb(); + return new FluxEngineUsb(device); } diff --git a/lib/usb/usb.cc b/lib/usb/usb.cc index acf29d8b..ba6badfb 100644 --- a/lib/usb/usb.cc +++ b/lib/usb/usb.cc @@ -1,4 +1,5 @@ #include "globals.h" +#include "flags.h" #include "usb.h" #include "protocol.h" #include "fluxmap.h" @@ -6,8 +7,28 @@ #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() {} @@ -16,10 +37,99 @@ std::string USB::usberror(int i) return libusb_strerror((libusb_error) i); } +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); + + 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 << " " << i.first << ": "; + switch (i.second->type) + { + case DEV_FLUXENGINE: std::cout << "FluxEngine"; + } + std::cout << '\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) - usb = createFluxengineUsb(); + { + 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 index 292c0c1c..e20b043c 100644 --- a/lib/usb/usb.h +++ b/lib/usb/usb.h @@ -2,6 +2,7 @@ #define USB_H #include "bytes.h" +#include "flags.h" class Fluxmap; class libusb_device_handle; @@ -29,9 +30,10 @@ protected: libusb_device_handle* _device; }; +extern FlagGroup usbFlags; extern USB& getUsb(); -extern USB* createFluxengineUsb(); +extern USB* createFluxengineUsb(libusb_device_handle* device); static inline int usbGetVersion() { return getUsb().getVersion(); } static inline void usbRecalibrate() { getUsb().recalibrate(); } diff --git a/src/fe-rpm.cc b/src/fe-rpm.cc index fe3d55d3..69e09933 100644 --- a/src/fe-rpm.cc +++ b/src/fe-rpm.cc @@ -4,7 +4,9 @@ #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 3591fba3..0cb80eeb 100644 --- a/src/fe-seek.cc +++ b/src/fe-seek.cc @@ -3,7 +3,9 @@ #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 08e6beca..b45a9a68 100644 --- a/src/fe-testbandwidth.cc +++ b/src/fe-testbandwidth.cc @@ -2,7 +2,9 @@ #include "flags.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 f8d6c477..61cb625b 100644 --- a/src/fe-testvoltages.cc +++ b/src/fe-testvoltages.cc @@ -4,7 +4,9 @@ #include "protocol.h" #include -static FlagGroup flags; +static FlagGroup flags = { + &usbFlags, +}; static std::string display_voltages(struct voltages& v) { From 85bc1637f2c68e36cef854b24f6fca0b58f3af47 Mon Sep 17 00:00:00 2001 From: David Given Date: Mon, 10 Aug 2020 23:12:33 +0200 Subject: [PATCH 5/6] Document the use of multiple FluxEngines. --- doc/using.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/doc/using.md b/doc/using.md index 0e365c96..6c3080c8 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 From 4855f825e24a83ace1d453e80ff6bfd3c26bcfa3 Mon Sep 17 00:00:00 2001 From: David Given Date: Mon, 10 Aug 2020 23:16:04 +0200 Subject: [PATCH 6/6] Show serial number on device connection, and improve the device listing a bit. --- lib/usb/usb.cc | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/usb/usb.cc b/lib/usb/usb.cc index ba6badfb..866068af 100644 --- a/lib/usb/usb.cc +++ b/lib/usb/usb.cc @@ -37,6 +37,15 @@ 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; @@ -75,6 +84,7 @@ static void open_device(CandidateDevice& candidate) 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); } @@ -90,14 +100,7 @@ static CandidateDevice& select_candidate(const std::maptype) - { - case DEV_FLUXENGINE: std::cout << "FluxEngine"; - } - std::cout << '\n'; - } + std::cout << " " << device_type(i.second->type) << ": " << i.first << '\n'; Error() << "specify USB device"; } else