Initial work towards Applesauce support --- you can connect to the device and

test bandwidth.
This commit is contained in:
David Given
2024-09-28 20:04:54 +02:00
parent 2840831017
commit 8fa1a887ce
9 changed files with 400 additions and 10 deletions

View File

@@ -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
View 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

View File

@@ -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});

View File

@@ -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);

View File

@@ -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;

View File

@@ -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");

View File

@@ -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()
{

View File

@@ -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"];
}

View File

@@ -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));
}