From 7efaae2f76e200ebeef74acb601aaae816d62063 Mon Sep 17 00:00:00 2001 From: David Given Date: Sat, 20 Oct 2018 13:28:20 +0200 Subject: [PATCH] Add the skeleton of the new client app. --- lib/flags.cc | 107 ++++++++++++++ lib/flags.h | 103 +++++++++++++ lib/globals.cc | 11 ++ lib/globals.h | 35 +++++ lib/reader.cc | 23 +++ lib/reader.h | 6 + lib/usb.cc | 224 +++++++++++++++++++++++++++++ lib/usb.h | 9 ++ oldclient/protocol.h => protocol.h | 0 src/fe-readibm.cc | 11 ++ src/fe-rpm.cc | 13 ++ src/fe-seek.cc | 16 +++ src/fe-testbulktransport.cc | 10 ++ 13 files changed, 568 insertions(+) create mode 100644 lib/flags.cc create mode 100644 lib/flags.h create mode 100644 lib/globals.cc create mode 100644 lib/globals.h create mode 100644 lib/reader.cc create mode 100644 lib/reader.h create mode 100644 lib/usb.cc create mode 100644 lib/usb.h rename oldclient/protocol.h => protocol.h (100%) create mode 100644 src/fe-readibm.cc create mode 100644 src/fe-rpm.cc create mode 100644 src/fe-seek.cc create mode 100644 src/fe-testbulktransport.cc diff --git a/lib/flags.cc b/lib/flags.cc new file mode 100644 index 00000000..2feadfd1 --- /dev/null +++ b/lib/flags.cc @@ -0,0 +1,107 @@ +#include "globals.h" +#include "flags.h" + +static std::vector all_flags; +static std::map flags_by_name; + +Flag::Flag(const std::vector& names, const std::string helptext): + _names(names), + _helptext(helptext) +{ + for (auto& name : names) + { + if (flags_by_name.find(name) != flags_by_name.end()) + Error() << "two flags use the name '" << name << "'"; + flags_by_name[name] = this; + } + + all_flags.push_back(this); +} + +void Flag::parseFlags(int argc, const char* argv[]) +{ + int index = 1; + while (index < argc) + { + std::string thisarg = argv[index]; + std::string thatarg = (index < (argc-1)) ? argv[index+1] : ""; + + std::string key; + std::string value; + bool usesthat = false; + + if ((thisarg.size() == 0) || (thisarg[0] != '-')) + Error() << "non-option parameter " << thisarg << " seen (try --help)"; + if ((thisarg.size() > 1) && (thisarg[1] == '-')) + { + /* Long option. */ + + auto equals = thisarg.rfind('='); + if (equals != std::string::npos) + { + key = thisarg.substr(0, equals); + value = thisarg.substr(equals+1); + } + else + { + key = thisarg; + value = thatarg; + usesthat = true; + } + } + else + { + /* Short option. */ + + if (thisarg.size() > 2) + { + key = thisarg.substr(0, 2); + value = thisarg.substr(2); + } + else + { + key = thisarg; + value = thatarg; + usesthat = true; + } + } + + auto flag = flags_by_name.find(key); + if (flag == flags_by_name.end()) + Error() << "unknown flag '" << key << "'; try --help"; + + flag->second->set(value); + + index++; + if (usesthat && flag->second->hasArgument()) + index++; + } + +} + +static void doHelp() +{ + std::cout << "FluxEngine options:" << std::endl; + for (auto flag : all_flags) + { + std::cout << " "; + bool firstname = true; + for (auto name : flag->names()) + { + if (!firstname) + std::cout << ", "; + std::cout << name; + firstname = false; + } + + if (flag->hasArgument()) + std::cout << " defaultValue() << "\">"; + std::cout << ": " << flag->helptext() << std::endl; + } + exit(0); +} + +static ActionFlag helpFlag = ActionFlag( + { "--help", "-h" }, + "Shows the help.", + doHelp); \ No newline at end of file diff --git a/lib/flags.h b/lib/flags.h new file mode 100644 index 00000000..5f2c42ed --- /dev/null +++ b/lib/flags.h @@ -0,0 +1,103 @@ +#ifndef FLAGS_H +#define FLAGS_H + +class Flag +{ +public: + static void parseFlags(int argc, const char* argv[]); + + Flag(const std::vector& names, const std::string helptext); + virtual ~Flag() {}; + + const std::string& name() const { return _names[0]; } + const std::vector names() const { return _names; } + const std::string& helptext() const { return _helptext; } + + virtual bool hasArgument() const = 0; + virtual const std::string defaultValue() const = 0; + virtual void set(const std::string value) = 0; + +private: + const std::vector _names; + const std::string _helptext; +}; + +class ActionFlag : Flag +{ +public: + ActionFlag(const std::vector& names, const std::string helptext, + std::function callback): + Flag(names, helptext), + _callback(callback) + {} + + bool hasArgument() const { return false; } + const std::string defaultValue() const { return ""; } + void set(const std::string value) { _callback(); } + +private: + const std::function _callback; +}; + +class SettableFlag : public Flag +{ +public: + SettableFlag(const std::vector& names, const std::string helptext): + Flag(names, helptext) + {} + + operator bool() const { return _value; } + + bool hasArgument() const { return false; } + const std::string defaultValue() const { return "false"; } + void set(const std::string value) { _value = true; } + +private: + bool _value = false; +}; + +template +class ValueFlag : public Flag +{ +public: + ValueFlag(const std::vector& names, const std::string helptext, + const T defaultValue): + Flag(names, helptext), + _defaultValue(defaultValue), + _value(defaultValue) + {} + + operator T() const { return _value; } + + bool hasArgument() const { return true; } + +protected: + T _defaultValue; + T _value; +}; + +class StringFlag : public ValueFlag +{ +public: + StringFlag(const std::vector& names, const std::string helptext, + const std::string defaultValue = ""): + ValueFlag(names, helptext, defaultValue) + {} + + const std::string defaultValue() const { return _defaultValue; } + void set(const std::string value) { _value = value; } +}; + +class IntFlag : public ValueFlag +{ +public: + IntFlag(const std::vector& names, const std::string helptext, + int defaultValue = 0): + ValueFlag(names, helptext, defaultValue) + {} + + const std::string defaultValue() const { return std::to_string(_defaultValue); } + void set(const std::string value) { _value = std::stoi(value); } +}; + +#endif diff --git a/lib/globals.cc b/lib/globals.cc new file mode 100644 index 00000000..616adb28 --- /dev/null +++ b/lib/globals.cc @@ -0,0 +1,11 @@ +#include "globals.h" +#include + +double getCurrentTime(void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + + return double(tv.tv_sec) + tv.tv_usec/1000000.0; +} + diff --git a/lib/globals.h b/lib/globals.h new file mode 100644 index 00000000..cd53b5fd --- /dev/null +++ b/lib/globals.h @@ -0,0 +1,35 @@ +#ifndef GLOBALS_H +#define GLOBALS_H + +#include +#include +#include +#include +#include +#include + +typedef int nanoseconds_t; + +extern double getCurrentTime(); + +class Error +{ +public: + ~Error() + { + std::cerr << "Error: " << _stream.str() << std::endl; + exit(1); + } + + template + Error& operator<<(T&& t) + { + _stream << t; + return *this; + } + +private: + std::stringstream _stream; +}; + +#endif diff --git a/lib/reader.cc b/lib/reader.cc new file mode 100644 index 00000000..91a988f9 --- /dev/null +++ b/lib/reader.cc @@ -0,0 +1,23 @@ +#include "globals.h" +#include "flags.h" +#include "usb.h" + +static StringFlag source( + { "--source", "-s" }, + "source for data", + ""); + +static StringFlag destination( + { "--write-flux", "-f" }, + "write the raw magnetic flux to this file", + ""); + +static SettableFlag justRead( + { "--just-read", "-R" }, + "just read the disk but do no further processing"); + +int allTracks() +{ + return 0; +} + diff --git a/lib/reader.h b/lib/reader.h new file mode 100644 index 00000000..2bb9cd3f --- /dev/null +++ b/lib/reader.h @@ -0,0 +1,6 @@ +#ifndef READER_H +#define READER_H + +extern int allTracks(); + +#endif diff --git a/lib/usb.cc b/lib/usb.cc new file mode 100644 index 00000000..40e96b8a --- /dev/null +++ b/lib/usb.cc @@ -0,0 +1,224 @@ +#include "globals.h" +#include "usb.h" +#include "protocol.h" +#include + +#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() << "this version of the client is too old for this FluxEngine"; +} + +static int usb_cmd_send(void* ptr, int 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); +} + +static void bad_reply(void) +{ + struct error_frame* f = (struct error_frame*) buffer; + if (f->f.type != F_FRAME_ERROR) + Error() << "bad USB reply %d" << 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() << "unknown device error " << f->error; + } +} + +template +static T* await_reply(int desired) +{ + usb_cmd_recv(buffer, sizeof(buffer)); + struct any_frame* r = (struct any_frame*) buffer; + 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(uint8_t track) +{ + usb_init(); + + struct seek_frame f = { + { .type = F_FRAME_SEEK_CMD, .size = sizeof(f) }, + .track = track + }; + usb_cmd_send(&f, f.f.size); + await_reply(F_FRAME_SEEK_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 * 1000; +} + +static int large_bulk_transfer(int ep, void* buffer, int total_len) +{ + int len; + int i = libusb_bulk_transfer(device, ep, (uint8_t*) buffer, total_len, &len, TIMEOUT); + if (i < 0) + Error() << "data transfer failed: " << usberror(i); + return len; +} + +void usbTestBulkTransport() +{ + usb_init(); + + struct any_frame f = { .f = {.type = F_FRAME_BULK_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; + + uint8_t bulk_buffer[XSIZE*YSIZE*ZSIZE]; + int total_len = sizeof(bulk_buffer); + double start_time = getCurrentTime(); + large_bulk_transfer(FLUXENGINE_DATA_IN_EP, bulk_buffer, total_len); + double elapsed_time = getCurrentTime() - start_time; + + std::cout << "Transferred " + << total_len + << " bytes in " + << int(elapsed_time * 1000.0) + << " (" + << int((total_len / 1024.0) / elapsed_time) + << " kB/s)" + << std::endl; + + for (int x=0; x(F_FRAME_BULK_TEST_REPLY); +} + +#if 0 +struct fluxmap* usb_read(int side, int revolutions) +{ + struct read_frame f = { + .f = { .type = F_FRAME_READ_CMD, .size = sizeof(f) }, + .side = side, + .revolutions = revolutions + }; + + struct fluxmap* fluxmap = create_fluxmap(); + usb_cmd_send(&f, f.f.size); + + uint8_t buffer[1024*1024]; + int len = large_bulk_transfer(FLUXENGINE_DATA_IN_EP, buffer, sizeof(buffer)); + + fluxmap_append_intervals(fluxmap, buffer, len); + + await_reply(F_FRAME_READ_REPLY); + return fluxmap; +} + +/* Returns number of bytes actually written */ +void usb_write(int side, struct fluxmap* fluxmap) +{ + int safelen = fluxmap->bytes & ~(FRAME_SIZE-1); + + /* Convert from intervals to absolute timestamps. */ + + uint8_t buffer[1024*1024]; + uint8_t clock = 0; + for (int i=0; iintervals[i]; + buffer[i] = clock; + } + + struct write_frame f = { + .f = { .type = F_FRAME_WRITE_CMD, .size = sizeof(f) }, + .side = side, + .bytes_to_write = htole32(safelen), + }; + usb_cmd_send(&f, f.f.size); + + large_bulk_transfer(FLUXENGINE_DATA_OUT_EP, buffer, safelen); + + await_reply(F_FRAME_WRITE_REPLY); +} +#endif diff --git a/lib/usb.h b/lib/usb.h new file mode 100644 index 00000000..152cbc1d --- /dev/null +++ b/lib/usb.h @@ -0,0 +1,9 @@ +#ifndef USB_H +#define USB_H + +extern int usbGetVersion(); +extern void usbSeek(uint8_t track); +extern nanoseconds_t usbGetRotationalPeriod(); +extern void usbTestBulkTransport(); + +#endif diff --git a/oldclient/protocol.h b/protocol.h similarity index 100% rename from oldclient/protocol.h rename to protocol.h diff --git a/src/fe-readibm.cc b/src/fe-readibm.cc new file mode 100644 index 00000000..3789c304 --- /dev/null +++ b/src/fe-readibm.cc @@ -0,0 +1,11 @@ +#include "globals.h" +#include "flags.h" +#include "reader.h" + +int main(int argc, const char* argv[]) +{ + Flag::parseFlags(argc, argv); + allTracks(); + return 0; +} + diff --git a/src/fe-rpm.cc b/src/fe-rpm.cc new file mode 100644 index 00000000..a8a695f1 --- /dev/null +++ b/src/fe-rpm.cc @@ -0,0 +1,13 @@ +#include "globals.h" +#include "flags.h" +#include "usb.h" + +int main(int argc, const char* argv[]) +{ + Flag::parseFlags(argc, argv); + + nanoseconds_t period = usbGetRotationalPeriod(); + std::cout << "Rotational period is " << period/1000 << " ms (" << 60e6/period << " rpm)" << std::endl; + + return 0; +} diff --git a/src/fe-seek.cc b/src/fe-seek.cc new file mode 100644 index 00000000..4363a493 --- /dev/null +++ b/src/fe-seek.cc @@ -0,0 +1,16 @@ +#include "globals.h" +#include "flags.h" +#include "usb.h" + +static IntFlag track( + { "--track", "-t" }, + "track to seek to", + 0); + +int main(int argc, const char* argv[]) +{ + Flag::parseFlags(argc, argv); + + usbSeek(track); + return 0; +} diff --git a/src/fe-testbulktransport.cc b/src/fe-testbulktransport.cc new file mode 100644 index 00000000..73c87b60 --- /dev/null +++ b/src/fe-testbulktransport.cc @@ -0,0 +1,10 @@ +#include "globals.h" +#include "flags.h" +#include "usb.h" + +int main(int argc, const char* argv[]) +{ + Flag::parseFlags(argc, argv); + usbTestBulkTransport(); + return 0; +}