mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
Initial work towards Applesauce support --- you can connect to the device and
test bandwidth.
This commit is contained in:
1
build.py
1
build.py
@@ -90,6 +90,7 @@ cxxlibrary(
|
||||
"./lib/proto.cc",
|
||||
"./lib/readerwriter.cc",
|
||||
"./lib/sector.cc",
|
||||
"./lib/usb/applesauceusb.cc",
|
||||
"./lib/usb/fluxengineusb.cc",
|
||||
"./lib/usb/greaseweazle.cc",
|
||||
"./lib/usb/greaseweazleusb.cc",
|
||||
|
||||
335
lib/usb/applesauceusb.cc
Normal file
335
lib/usb/applesauceusb.cc
Normal file
@@ -0,0 +1,335 @@
|
||||
#include "lib/globals.h"
|
||||
#include "protocol.h"
|
||||
#include "lib/fluxmap.h"
|
||||
#include "lib/bytes.h"
|
||||
#include "lib/usb/usb.pb.h"
|
||||
#include "applesauce.h"
|
||||
#include "serial.h"
|
||||
#include "usb.h"
|
||||
#include <unistd.h>
|
||||
|
||||
static uint32_t ss_rand_next(uint32_t x)
|
||||
{
|
||||
return (x & 1) ? (x >> 1) ^ 0x80000062 : x >> 1;
|
||||
}
|
||||
|
||||
class ApplesauceUsb : public USB
|
||||
{
|
||||
public:
|
||||
ApplesauceUsb(const std::string& port, const ApplesauceProto& config):
|
||||
_serial(SerialPort::openSerialPort(port)),
|
||||
_config(config)
|
||||
{
|
||||
std::string s = sendrecv("?");
|
||||
if (s != "Applesauce")
|
||||
error(
|
||||
"Applesauce device not responding (expected 'Applesauce', got "
|
||||
"'{}')",
|
||||
s);
|
||||
}
|
||||
|
||||
~ApplesauceUsb()
|
||||
{
|
||||
sendrecv("disconnect");
|
||||
}
|
||||
|
||||
private:
|
||||
std::string sendrecv(const std::string& command)
|
||||
{
|
||||
_serial->writeLine(command);
|
||||
return _serial->readLine();
|
||||
}
|
||||
|
||||
void connect()
|
||||
{
|
||||
if (!_connected)
|
||||
{
|
||||
std::string probe = sendrecv("connect");
|
||||
if (probe != ".")
|
||||
error("Applesauce could not find any drives");
|
||||
_connected = true;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
int getVersion() override
|
||||
{
|
||||
// do_command({CMD_GET_INFO, 3, GETINFO_FIRMWARE});
|
||||
//
|
||||
// Bytes response = _serial->readBytes(32);
|
||||
// ByteReader br(response);
|
||||
//
|
||||
// br.seek(4);
|
||||
// nanoseconds_t freq = br.read_le32();
|
||||
// _clock = 1000000000 / freq;
|
||||
//
|
||||
// br.seek(0);
|
||||
// return br.read_be16();
|
||||
error("unsupported operation getVersion on the Greaseweazle");
|
||||
}
|
||||
|
||||
void seek(int track) override
|
||||
{
|
||||
// do_command({CMD_SEEK, 3, (uint8_t)track});
|
||||
error("unsupported operation seek on the Greaseweazle");
|
||||
}
|
||||
|
||||
nanoseconds_t getRotationalPeriod(int hardSectorCount) override
|
||||
{
|
||||
// if (hardSectorCount != 0)
|
||||
// error("hard sectors are currently unsupported on the
|
||||
// Greaseweazle");
|
||||
|
||||
// /* The Greaseweazle doesn't have a command to fetch the period
|
||||
// directly,
|
||||
// * so we have to do a flux read. */
|
||||
|
||||
// switch (_version)
|
||||
// {
|
||||
// case V22:
|
||||
// do_command({CMD_READ_FLUX, 2});
|
||||
// break;
|
||||
|
||||
// case V24:
|
||||
// case V29:
|
||||
// {
|
||||
// Bytes cmd(8);
|
||||
// cmd.writer()
|
||||
// .write_8(CMD_READ_FLUX)
|
||||
// .write_8(cmd.size())
|
||||
// .write_le32(0) // ticks default value (guessed)
|
||||
// .write_le16(2); // revolutions
|
||||
// do_command(cmd);
|
||||
// }
|
||||
// }
|
||||
|
||||
// uint32_t ticks_gw = 0;
|
||||
// uint32_t firstindex = ~0;
|
||||
// uint32_t secondindex = ~0;
|
||||
// for (;;)
|
||||
// {
|
||||
// uint8_t b = _serial->readByte();
|
||||
// if (!b)
|
||||
// break;
|
||||
|
||||
// if (b == 255)
|
||||
// {
|
||||
// switch (_serial->readByte())
|
||||
// {
|
||||
// case FLUXOP_INDEX:
|
||||
// {
|
||||
// uint32_t index = read_28() + ticks_gw;
|
||||
// if (firstindex == ~0)
|
||||
// firstindex = index;
|
||||
// else if (secondindex == ~0)
|
||||
// secondindex = index;
|
||||
// break;
|
||||
// }
|
||||
|
||||
// case FLUXOP_SPACE:
|
||||
// ticks_gw += read_28();
|
||||
// break;
|
||||
|
||||
// default:
|
||||
// error("bad opcode in Greaseweazle stream");
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// if (b < 250)
|
||||
// ticks_gw += b;
|
||||
// else
|
||||
// {
|
||||
// int delta = 250 + (b - 250) * 255 + _serial->readByte() -
|
||||
// 1; ticks_gw += delta;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (secondindex == ~0)
|
||||
// error(
|
||||
// "unable to determine disk rotational period (is a disk in the
|
||||
// " "drive?)");
|
||||
// do_command({CMD_GET_FLUX_STATUS, 2});
|
||||
|
||||
// _revolutions = (nanoseconds_t)(secondindex - firstindex) * _clock;
|
||||
// return _revolutions;
|
||||
error("unsupported operation getRotationalPeriod on the Greaseweazle");
|
||||
}
|
||||
|
||||
void testBulkWrite() override
|
||||
{
|
||||
int max = std::stoi(sendrecv("data:?max"));
|
||||
fmt::print("Writing data: ");
|
||||
|
||||
if (sendrecv(fmt::format("data:>{}", max)) != ".")
|
||||
error("Cannot write to Applesauce");
|
||||
|
||||
Bytes junk(max);
|
||||
uint32_t seed = 0;
|
||||
for (int i = 0; i < max; i++)
|
||||
{
|
||||
junk[i] = seed;
|
||||
seed = ss_rand_next(seed);
|
||||
}
|
||||
double start_time = getCurrentTime();
|
||||
_serial->write(junk);
|
||||
_serial->readLine();
|
||||
double elapsed_time = getCurrentTime() - start_time;
|
||||
|
||||
std::cout << fmt::format(
|
||||
"transferred {} bytes from PC -> device in {} ms ({} kb/s)\n",
|
||||
max,
|
||||
int(elapsed_time * 1000.0),
|
||||
int((max / 1024.0) / elapsed_time));
|
||||
}
|
||||
|
||||
void testBulkRead() override
|
||||
{
|
||||
int max = std::stoi(sendrecv("data:?max"));
|
||||
fmt::print("Reading data: ");
|
||||
|
||||
if (sendrecv(fmt::format("data:<{}", max)) != ".")
|
||||
error("Cannot read from Applesauce");
|
||||
|
||||
double start_time = getCurrentTime();
|
||||
_serial->readBytes(max);
|
||||
double elapsed_time = getCurrentTime() - start_time;
|
||||
|
||||
std::cout << fmt::format(
|
||||
"transferred {} bytes from device -> PC in {} ms ({} kb/s)\n",
|
||||
max,
|
||||
int(elapsed_time * 1000.0),
|
||||
int((max / 1024.0) / elapsed_time));
|
||||
}
|
||||
|
||||
Bytes read(int side,
|
||||
bool synced,
|
||||
nanoseconds_t readTime,
|
||||
nanoseconds_t hardSectorThreshold) override
|
||||
{
|
||||
// if (hardSectorThreshold != 0)
|
||||
// error("hard sectors are currently unsupported on the Greaseweazle");
|
||||
//
|
||||
// do_command({CMD_HEAD, 3, (uint8_t)side});
|
||||
//
|
||||
// switch (_version)
|
||||
// {
|
||||
// case V22:
|
||||
// {
|
||||
// int revolutions = (readTime + _revolutions - 1) / _revolutions;
|
||||
// Bytes cmd(4);
|
||||
// cmd.writer()
|
||||
// .write_8(CMD_READ_FLUX)
|
||||
// .write_8(cmd.size())
|
||||
// .write_le32(revolutions + (synced ? 1 : 0));
|
||||
// do_command(cmd);
|
||||
// break;
|
||||
// }
|
||||
//
|
||||
// case V24:
|
||||
// case V29:
|
||||
// {
|
||||
// Bytes cmd(8);
|
||||
// cmd.writer()
|
||||
// .write_8(CMD_READ_FLUX)
|
||||
// .write_8(cmd.size())
|
||||
// .write_le32(
|
||||
// (readTime + (synced ? _revolutions : 0)) / _clock)
|
||||
// .write_le16(0);
|
||||
// do_command(cmd);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Bytes buffer;
|
||||
// ByteWriter bw(buffer);
|
||||
// for (;;)
|
||||
// {
|
||||
// uint8_t b = _serial->readByte();
|
||||
// if (!b)
|
||||
// break;
|
||||
// bw.write_8(b);
|
||||
// }
|
||||
//
|
||||
// do_command({CMD_GET_FLUX_STATUS, 2});
|
||||
//
|
||||
// Bytes fldata = greaseWeazleToFluxEngine(buffer, _clock);
|
||||
// if (synced)
|
||||
// fldata = stripPartialRotation(fldata);
|
||||
// return fldata;
|
||||
error("unsupported operation read on the Greaseweazle");
|
||||
}
|
||||
|
||||
void write(int side,
|
||||
const Bytes& fldata,
|
||||
nanoseconds_t hardSectorThreshold) override
|
||||
{
|
||||
// if (hardSectorThreshold != 0)
|
||||
// error("hard sectors are currently unsupported on the Greaseweazle");
|
||||
//
|
||||
// do_command({CMD_HEAD, 3, (uint8_t)side});
|
||||
// switch (_version)
|
||||
// {
|
||||
// case V22:
|
||||
// do_command({CMD_WRITE_FLUX, 3, 1});
|
||||
// break;
|
||||
//
|
||||
// case V24:
|
||||
// case V29:
|
||||
// do_command({CMD_WRITE_FLUX, 4, 1, 1});
|
||||
// break;
|
||||
// }
|
||||
// _serial->write(fluxEngineToGreaseweazle(fldata, _clock));
|
||||
// _serial->readByte(); /* synchronise */
|
||||
//
|
||||
// do_command({CMD_GET_FLUX_STATUS, 2});
|
||||
error("unsupported operation write on the Greaseweazle");
|
||||
}
|
||||
|
||||
void erase(int side, nanoseconds_t hardSectorThreshold) override
|
||||
{
|
||||
// if (hardSectorThreshold != 0)
|
||||
// error("hard sectors are currently unsupported on the Greaseweazle");
|
||||
//
|
||||
// do_command({CMD_HEAD, 3, (uint8_t)side});
|
||||
//
|
||||
// Bytes cmd(6);
|
||||
// ByteWriter bw(cmd);
|
||||
// bw.write_8(CMD_ERASE_FLUX);
|
||||
// bw.write_8(cmd.size());
|
||||
// bw.write_le32(200e6 / _clock);
|
||||
// do_command(cmd);
|
||||
// _serial->readByte(); /* synchronise */
|
||||
//
|
||||
// do_command({CMD_GET_FLUX_STATUS, 2});
|
||||
error("unsupported operation erase on the Greaseweazle");
|
||||
}
|
||||
|
||||
void setDrive(int drive, bool high_density, int index_mode) override
|
||||
{
|
||||
if (drive != 0)
|
||||
error("the Applesauce only supports drive 0");
|
||||
|
||||
connect();
|
||||
sendrecv("drive:enable");
|
||||
sendrecv("motor:on");
|
||||
sendrecv(fmt::format("dpc:density{}", high_density));
|
||||
}
|
||||
|
||||
void measureVoltages(struct voltages_frame* voltages) override
|
||||
{
|
||||
error("unsupported operation on the Greaseweazle");
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<SerialPort> _serial;
|
||||
const ApplesauceProto& _config;
|
||||
bool _connected = false;
|
||||
};
|
||||
|
||||
USB* createApplesauceUsb(const std::string& port, const ApplesauceProto& config)
|
||||
{
|
||||
return new ApplesauceUsb(port, config);
|
||||
}
|
||||
|
||||
// vim: sw=4 ts=4 et
|
||||
@@ -124,11 +124,6 @@ public:
|
||||
return br.read_be16();
|
||||
}
|
||||
|
||||
void recalibrate() override
|
||||
{
|
||||
seek(0);
|
||||
}
|
||||
|
||||
void seek(int track) override
|
||||
{
|
||||
do_command({CMD_SEEK, 3, (uint8_t)track});
|
||||
|
||||
@@ -254,6 +254,11 @@ uint8_t SerialPort::readByte()
|
||||
return b;
|
||||
}
|
||||
|
||||
void SerialPort::writeByte(uint8_t b)
|
||||
{
|
||||
this->write(&b, 1);
|
||||
}
|
||||
|
||||
void SerialPort::write(const Bytes& bytes)
|
||||
{
|
||||
int ptr = 0;
|
||||
@@ -264,6 +269,28 @@ void SerialPort::write(const Bytes& bytes)
|
||||
}
|
||||
}
|
||||
|
||||
void SerialPort::writeLine(const std::string& chars)
|
||||
{
|
||||
this->write((const uint8_t*)&chars[0], chars.size());
|
||||
writeByte('\n');
|
||||
}
|
||||
|
||||
std::string SerialPort::readLine()
|
||||
{
|
||||
std::string s;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
uint8_t b = readByte();
|
||||
if (b == '\r')
|
||||
continue;
|
||||
if (b == '\n')
|
||||
return s;
|
||||
|
||||
s += b;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<SerialPort> SerialPort::openSerialPort(const std::string& path)
|
||||
{
|
||||
return std::make_unique<SerialPortImpl>(path);
|
||||
|
||||
@@ -17,8 +17,12 @@ public:
|
||||
void read(Bytes& bytes);
|
||||
Bytes readBytes(size_t count);
|
||||
uint8_t readByte();
|
||||
void writeByte(uint8_t b);
|
||||
void write(const Bytes& bytes);
|
||||
|
||||
void writeLine(const std::string& chars);
|
||||
std::string readLine();
|
||||
|
||||
private:
|
||||
uint8_t _readbuffer[4096];
|
||||
size_t _readbuffer_ptr = 0;
|
||||
|
||||
@@ -77,6 +77,14 @@ USB* get_usb_impl()
|
||||
return createGreaseweazleUsb(conf.port(), conf);
|
||||
}
|
||||
|
||||
if (globalConfig()->usb().has_applesauce() &&
|
||||
globalConfig()->usb().applesauce().has_port())
|
||||
{
|
||||
const auto& conf = globalConfig()->usb().applesauce();
|
||||
log("Using Applesauce on serial port {}", conf.port());
|
||||
return createApplesauceUsb(conf.port(), conf);
|
||||
}
|
||||
|
||||
/* Otherwise, select a device by USB ID. */
|
||||
|
||||
auto candidate = selectDevice();
|
||||
@@ -94,7 +102,11 @@ USB* get_usb_impl()
|
||||
candidate->serialPort, globalConfig()->usb().greaseweazle());
|
||||
|
||||
case APPLESAUCE_ID:
|
||||
error("Applesauce not supported yet");
|
||||
log("Using Applesauce {} on {}",
|
||||
candidate->serial,
|
||||
candidate->serialPort);
|
||||
return createApplesauceUsb(
|
||||
candidate->serialPort, globalConfig()->usb().applesauce());
|
||||
|
||||
default:
|
||||
error("internal");
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
class Fluxmap;
|
||||
class GreaseweazleProto;
|
||||
class ApplesauceProto;
|
||||
namespace libusbp
|
||||
{
|
||||
class device;
|
||||
@@ -17,7 +18,10 @@ public:
|
||||
virtual ~USB();
|
||||
|
||||
virtual int getVersion() = 0;
|
||||
virtual void recalibrate() = 0;
|
||||
virtual void recalibrate()
|
||||
{
|
||||
seek(0);
|
||||
};
|
||||
virtual void seek(int track) = 0;
|
||||
virtual nanoseconds_t getRotationalPeriod(int hardSectorCount) = 0;
|
||||
virtual void testBulkWrite() = 0;
|
||||
@@ -41,6 +45,8 @@ extern USB& getUsb();
|
||||
extern USB* createFluxengineUsb(libusbp::device& device);
|
||||
extern USB* createGreaseweazleUsb(
|
||||
const std::string& serialPort, const GreaseweazleProto& config);
|
||||
extern USB* createApplesauceUsb(
|
||||
const std::string& serialPort, const ApplesauceProto& config);
|
||||
|
||||
static inline int usbGetVersion()
|
||||
{
|
||||
|
||||
@@ -16,9 +16,15 @@ message GreaseweazleProto {
|
||||
[(help) = "which FDD bus type is in use", default = IBMPC];
|
||||
}
|
||||
|
||||
message ApplesauceProto {
|
||||
optional string port = 1
|
||||
[(help) = "Applesauce serial port to use"];
|
||||
}
|
||||
|
||||
message UsbProto {
|
||||
optional string serial = 1
|
||||
[(help) = "serial number of FluxEngine or Greaseweazle device to use"];
|
||||
|
||||
optional GreaseweazleProto greaseweazle = 2 [(help) = "Greaseweazle-specific options"];
|
||||
optional ApplesauceProto applesauce = 3 [(help) = "Applesauce-specific options"];
|
||||
}
|
||||
|
||||
@@ -42,13 +42,17 @@ std::vector<std::shared_ptr<CandidateDevice>> findUsbDevices()
|
||||
candidate->serial = get_serial_number(it);
|
||||
|
||||
if (id == GREASEWEAZLE_ID)
|
||||
candidate->type = DEVICE_GREASEWEAZLE;
|
||||
else if (id == APPLESAUCE_ID)
|
||||
candidate->type = DEVICE_APPLESAUCE;
|
||||
else if (id == FLUXENGINE_ID)
|
||||
candidate->type = DEVICE_FLUXENGINE;
|
||||
|
||||
if ((id == GREASEWEAZLE_ID) || (id == APPLESAUCE_ID))
|
||||
{
|
||||
libusbp::serial_port port(candidate->device);
|
||||
candidate->serialPort = port.get_name();
|
||||
candidate->type = DEVICE_GREASEWEAZLE;
|
||||
}
|
||||
else if (id == FLUXENGINE_ID)
|
||||
candidate->type = DEVICE_FLUXENGINE;
|
||||
|
||||
candidates.push_back(std::move(candidate));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user