mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
Merge from master.
This commit is contained in:
@@ -138,6 +138,9 @@ public:
|
||||
auto bits = readRawBits(recordSize*16);
|
||||
auto bytes = decodeFmMfm(bits).slice(0, recordSize);
|
||||
|
||||
IbmDecoderProto::TrackdataProto trackdata;
|
||||
getTrackFormat(trackdata, _sector->physicalCylinder, _sector->physicalHead);
|
||||
|
||||
ByteReader br(bytes);
|
||||
br.seek(_currentHeaderLength);
|
||||
br.read_8(); /* skip ID byte */
|
||||
@@ -150,11 +153,11 @@ public:
|
||||
if (wantCrc == gotCrc)
|
||||
_sector->status = Sector::DATA_MISSING; /* correct but unintuitive */
|
||||
|
||||
if (_config.swap_sides())
|
||||
if (trackdata.swap_sides())
|
||||
_sector->logicalSide ^= 1;
|
||||
if (_config.ignore_side_byte())
|
||||
if (trackdata.ignore_side_byte())
|
||||
_sector->logicalSide = _sector->physicalHead;
|
||||
if (_config.ignore_track_byte())
|
||||
if (trackdata.ignore_track_byte())
|
||||
_sector->logicalTrack = _sector->physicalCylinder;
|
||||
}
|
||||
|
||||
@@ -176,12 +179,42 @@ public:
|
||||
|
||||
std::set<unsigned> requiredSectors(unsigned cylinder, unsigned head) const override
|
||||
{
|
||||
IbmDecoderProto::TrackdataProto trackdata;
|
||||
getTrackFormat(trackdata, cylinder, head);
|
||||
|
||||
std::set<unsigned> s;
|
||||
for (int sectorId : _config.sectors().sector())
|
||||
s.insert(sectorId);
|
||||
if (trackdata.has_sectors())
|
||||
{
|
||||
for (int sectorId : trackdata.sectors().sector())
|
||||
s.insert(sectorId);
|
||||
}
|
||||
else if (trackdata.has_sector_range())
|
||||
{
|
||||
int sectorId = trackdata.sector_range().min_sector();
|
||||
while (sectorId <= trackdata.sector_range().max_sector())
|
||||
{
|
||||
s.insert(sectorId);
|
||||
sectorId++;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
private:
|
||||
void getTrackFormat(IbmDecoderProto::TrackdataProto& trackdata, unsigned cylinder, unsigned head) const
|
||||
{
|
||||
trackdata.Clear();
|
||||
for (const auto& f : _config.trackdata())
|
||||
{
|
||||
if (f.has_cylinder() && (f.cylinder() != cylinder))
|
||||
continue;
|
||||
if (f.has_head() && (f.head() != head))
|
||||
continue;
|
||||
|
||||
trackdata.MergeFrom(f);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const IbmDecoderProto& _config;
|
||||
unsigned _currentSectorSize;
|
||||
|
||||
@@ -101,6 +101,27 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static std::set<unsigned> getSectorIds(const IbmEncoderProto::TrackdataProto& trackdata)
|
||||
{
|
||||
std::set<unsigned> s;
|
||||
if (trackdata.has_sectors())
|
||||
{
|
||||
for (int sectorId : trackdata.sectors().sector())
|
||||
s.insert(sectorId);
|
||||
}
|
||||
else if (trackdata.has_sector_range())
|
||||
{
|
||||
int sectorId = trackdata.sector_range().min_sector();
|
||||
while (sectorId <= trackdata.sector_range().max_sector())
|
||||
{
|
||||
s.insert(sectorId);
|
||||
sectorId++;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public:
|
||||
std::vector<std::shared_ptr<Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
|
||||
{
|
||||
@@ -109,7 +130,7 @@ public:
|
||||
getTrackFormat(trackdata, physicalTrack, physicalSide);
|
||||
|
||||
int logicalSide = physicalSide ^ trackdata.swap_sides();
|
||||
for (int sectorId : trackdata.sectors().sector())
|
||||
for (int sectorId : getSectorIds(trackdata))
|
||||
{
|
||||
const auto& sector = image.get(physicalTrack, logicalSide, sectorId);
|
||||
if (sector)
|
||||
|
||||
@@ -2,24 +2,43 @@ syntax = "proto2";
|
||||
|
||||
import "lib/common.proto";
|
||||
|
||||
// Next: 7
|
||||
message IbmDecoderProto {
|
||||
message SectorsProto {
|
||||
repeated int32 sector = 1 [(help) = "require these sectors to exist for a good read"];
|
||||
// Next: 10
|
||||
message TrackdataProto {
|
||||
message SectorsProto {
|
||||
repeated int32 sector = 1 [(help) = "require these sectors to exist for a good read"];
|
||||
}
|
||||
message SectorRangeProto {
|
||||
optional int32 min_sector = 1 [(help) = "require these sectors to exist for a good read"];
|
||||
optional int32 max_sector = 2 [(help) = "require these sectors to exist for a good read"];
|
||||
}
|
||||
|
||||
optional int32 cylinder = 7 [(help) = "if set, the format applies only to this track"];
|
||||
optional int32 head = 8 [(help) = "if set, the format applies only to this head"];
|
||||
|
||||
optional bool ignore_side_byte = 2 [default = false, (help) = "ignore side byte in sector header"];
|
||||
optional bool ignore_track_byte = 6 [default = false, (help) = "ignore track byte in sector header"];
|
||||
optional bool swap_sides = 4 [default = false, (help) = "put logical side 1 on physical side 0"];
|
||||
|
||||
oneof required_sectors {
|
||||
SectorsProto sectors = 5 [(help) = "require these sectors to exist for a good read"];
|
||||
SectorRangeProto sector_range = 9 [(help) = "require these sectors to exist for a good read"];
|
||||
}
|
||||
}
|
||||
|
||||
optional bool ignore_side_byte = 2 [default = false, (help) = "ignore side byte in sector header"];
|
||||
optional bool ignore_track_byte = 6 [default = false, (help) = "ignore track byte in sector header"];
|
||||
optional bool swap_sides = 4 [default = false, (help) = "put logical side 1 on physical side 0"];
|
||||
optional SectorsProto sectors = 5 [(help) = "require these sectors to exist for a good read"];
|
||||
repeated TrackdataProto trackdata = 1;
|
||||
}
|
||||
|
||||
message IbmEncoderProto {
|
||||
// Next: 19
|
||||
// Next: 20
|
||||
message TrackdataProto {
|
||||
message SectorsProto {
|
||||
repeated int32 sector = 1 [(help) = "write these sectors (in order) on each track"];
|
||||
}
|
||||
message SectorRangeProto {
|
||||
optional int32 min_sector = 1 [(help) = "write these sectors (in order) on each track"];
|
||||
optional int32 max_sector = 2 [(help) = "write these sectors (in order) on each track"];
|
||||
}
|
||||
|
||||
optional int32 cylinder = 15 [(help) = "if set, the format applies only to this track"];
|
||||
optional int32 head = 16 [(help) = "if set, the format applies only to this head"];
|
||||
@@ -36,8 +55,12 @@ message IbmEncoderProto {
|
||||
optional int32 gap2 = 11 [default=22, (help) = "size of gap 3 (the pre-data gap)"];
|
||||
optional int32 gap3 = 12 [default=80, (help) = "size of gap 4 (the post-data or format gap)"];
|
||||
optional bool swap_sides = 14 [default=false, (help) = "swap side bytes when writing"];
|
||||
optional SectorsProto sectors = 17 [(help) = "write these sectors (in order) on each track"];
|
||||
optional int32 gap_fill_byte = 18 [default=0x9254, (help) = "16-bit raw bit pattern of gap fill byte"];
|
||||
|
||||
oneof required_sectors {
|
||||
SectorsProto sectors = 17 [(help) = "require these sectors to exist for a good read"];
|
||||
SectorRangeProto sector_range = 19 [(help) = "require these sectors to exist for a good read"];
|
||||
}
|
||||
}
|
||||
|
||||
repeated TrackdataProto trackdata = 1;
|
||||
|
||||
@@ -5,135 +5,7 @@
|
||||
#include "bytes.h"
|
||||
#include "fmt/format.h"
|
||||
#include "greaseweazle.h"
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#if defined __WIN32__
|
||||
#include <windows.h>
|
||||
|
||||
typedef HANDLE FileHandle;
|
||||
|
||||
static std::string get_last_error_string()
|
||||
{
|
||||
DWORD error = GetLastError();
|
||||
if (error == 0)
|
||||
return "OK";
|
||||
|
||||
LPSTR buffer = nullptr;
|
||||
size_t size = FormatMessageA(
|
||||
/* dwFlags= */ FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
/* lpSource= */ nullptr,
|
||||
/* dwMessageId= */ error,
|
||||
/* dwLanguageId= */ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
/* lpBuffer= */ (LPSTR) &buffer,
|
||||
/* nSize= */ 0,
|
||||
/* Arguments= */ nullptr);
|
||||
|
||||
std::string message(buffer, size);
|
||||
LocalFree(buffer);
|
||||
return message;
|
||||
}
|
||||
|
||||
static HANDLE open_serial_port(const std::string& name)
|
||||
{
|
||||
HANDLE h = CreateFileA(
|
||||
name.c_str(),
|
||||
/* dwDesiredAccess= */ GENERIC_READ|GENERIC_WRITE,
|
||||
/* dwShareMode= */ 0,
|
||||
/* lpSecurityAttribues= */ nullptr,
|
||||
/* dwCreationDisposition= */ OPEN_EXISTING,
|
||||
/* dwFlagsAndAttributes= */ FILE_ATTRIBUTE_NORMAL,
|
||||
/* hTemplateFile= */ nullptr);
|
||||
if (h == INVALID_HANDLE_VALUE)
|
||||
Error() << fmt::format("cannot open GreaseWeazle serial port '{}': {}",
|
||||
name, get_last_error_string());
|
||||
|
||||
DCB dcb =
|
||||
{
|
||||
.DCBlength = sizeof(DCB),
|
||||
.BaudRate = CBR_9600,
|
||||
.fBinary = true,
|
||||
.ByteSize = 8,
|
||||
.Parity = NOPARITY,
|
||||
.StopBits = ONESTOPBIT
|
||||
};
|
||||
SetCommState(h, &dcb);
|
||||
|
||||
COMMTIMEOUTS commtimeouts = {0};
|
||||
commtimeouts.ReadIntervalTimeout = 100;
|
||||
SetCommTimeouts(h, &commtimeouts);
|
||||
|
||||
PurgeComm(h, PURGE_RXABORT|PURGE_RXCLEAR|PURGE_TXABORT|PURGE_TXCLEAR);
|
||||
return h;
|
||||
}
|
||||
|
||||
static void close_serial_port(FileHandle h)
|
||||
{
|
||||
CloseHandle(h);
|
||||
}
|
||||
|
||||
static ssize_t read_serial_port(FileHandle h, uint8_t* buffer, size_t len)
|
||||
{
|
||||
DWORD rlen;
|
||||
bool r = ReadFile(
|
||||
/* hFile= */ h,
|
||||
/* lpBuffer= */ buffer,
|
||||
/* nNumberOfBytesToRead= */ len,
|
||||
/* lpNumberOfBytesRead= */ &rlen,
|
||||
/* lpOverlapped= */ nullptr);
|
||||
if (!r)
|
||||
return -1;
|
||||
return rlen;
|
||||
}
|
||||
|
||||
static ssize_t write_serial_port(FileHandle h, const uint8_t* buffer, size_t len)
|
||||
{
|
||||
DWORD wlen;
|
||||
bool r = WriteFile(
|
||||
/* hFile= */ h,
|
||||
/* lpBuffer= */ buffer,
|
||||
/* nNumberOfBytesToWrite= */ len,
|
||||
/* lpNumberOfBytesWritten= */ &wlen,
|
||||
/* lpOverlapped= */ nullptr);
|
||||
if (!r)
|
||||
return -1;
|
||||
return wlen;
|
||||
}
|
||||
|
||||
#else
|
||||
#include <termios.h>
|
||||
|
||||
typedef int FileHandle;
|
||||
static FileHandle open_serial_port(const std::string& name)
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
if (name.find("/dev/tty.") != std::string::npos)
|
||||
std::cerr << "Warning: you probably want to be using a /dev/cu.* device\n";
|
||||
#endif
|
||||
|
||||
int fd = open(name.c_str(), O_RDWR);
|
||||
if (fd == -1)
|
||||
Error() << fmt::format("cannot open GreaseWeazle serial port '{}': {}",
|
||||
name, strerror(errno));
|
||||
|
||||
struct termios t;
|
||||
tcgetattr(fd, &t);
|
||||
t.c_iflag = 0;
|
||||
t.c_oflag = 0;
|
||||
t.c_cflag = CREAD;
|
||||
t.c_lflag = 0;
|
||||
t.c_cc[VMIN] = 1;
|
||||
cfsetspeed(&t, 9600);
|
||||
tcsetattr(fd, TCSANOW, &t);
|
||||
return fd;
|
||||
}
|
||||
|
||||
#define close_serial_port(fd) ::close(fd)
|
||||
#define read_serial_port(fd, buf, len) ::read(fd, buf, len)
|
||||
#define write_serial_port(fd, buf, len) ::write(fd, buf, len)
|
||||
#endif
|
||||
#include "serial.h"
|
||||
|
||||
static const char* gw_error(int e)
|
||||
{
|
||||
@@ -158,86 +30,23 @@ static const char* gw_error(int e)
|
||||
class GreaseWeazleUsb : public USB
|
||||
{
|
||||
private:
|
||||
uint8_t _readbuffer[4096];
|
||||
int _readbuffer_ptr = 0;
|
||||
int _readbuffer_fill = 0;
|
||||
|
||||
void read_bytes(uint8_t* buffer, int len)
|
||||
{
|
||||
while (len > 0)
|
||||
{
|
||||
if (_readbuffer_ptr < _readbuffer_fill)
|
||||
{
|
||||
int buffered = std::min(len, _readbuffer_fill - _readbuffer_ptr);
|
||||
memcpy(buffer, _readbuffer + _readbuffer_ptr, buffered);
|
||||
_readbuffer_ptr += buffered;
|
||||
buffer += buffered;
|
||||
len -= buffered;
|
||||
}
|
||||
|
||||
if (len == 0)
|
||||
break;
|
||||
|
||||
ssize_t actual = read_serial_port(_fd, _readbuffer, sizeof(_readbuffer));
|
||||
if (actual < 0)
|
||||
Error() << "failed to receive command reply: " << strerror(actual);
|
||||
|
||||
_readbuffer_fill = actual;
|
||||
_readbuffer_ptr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void read_bytes(Bytes& bytes)
|
||||
{
|
||||
read_bytes(bytes.begin(), bytes.size());
|
||||
}
|
||||
|
||||
Bytes read_bytes(unsigned len)
|
||||
{
|
||||
Bytes b(len);
|
||||
read_bytes(b);
|
||||
return b;
|
||||
}
|
||||
|
||||
uint8_t read_byte()
|
||||
{
|
||||
uint8_t b;
|
||||
read_bytes(&b, 1);
|
||||
return b;
|
||||
}
|
||||
|
||||
uint32_t read_28()
|
||||
{
|
||||
return ((read_byte() & 0xfe) >> 1)
|
||||
| ((read_byte() & 0xfe) << 6)
|
||||
| ((read_byte() & 0xfe) << 13)
|
||||
| ((read_byte() & 0xfe) << 20);
|
||||
}
|
||||
uint8_t buffer[4];
|
||||
_serial->read(buffer, sizeof(buffer));
|
||||
|
||||
void write_bytes(const uint8_t* buffer, size_t len)
|
||||
{
|
||||
while (len > 0)
|
||||
{
|
||||
ssize_t actual = write_serial_port(_fd, buffer, len);
|
||||
if (actual < 0)
|
||||
Error() << "failed to send command: " << strerror(errno);
|
||||
|
||||
buffer += actual;
|
||||
len -= actual;
|
||||
}
|
||||
}
|
||||
|
||||
void write_bytes(const Bytes& bytes)
|
||||
{
|
||||
write_bytes(bytes.cbegin(), bytes.size());
|
||||
return ((buffer[0] & 0xfe) >> 1)
|
||||
| ((buffer[1] & 0xfe) << 6)
|
||||
| ((buffer[2] & 0xfe) << 13)
|
||||
| ((buffer[3] & 0xfe) << 20);
|
||||
}
|
||||
|
||||
void do_command(const Bytes& command)
|
||||
{
|
||||
write_bytes(command);
|
||||
_serial->write(command);
|
||||
|
||||
uint8_t buffer[2];
|
||||
read_bytes(buffer, sizeof(buffer));
|
||||
_serial->read(buffer, sizeof(buffer));
|
||||
|
||||
if (buffer[0] != command[0])
|
||||
Error() << fmt::format("command returned garbage (0x{:x} != 0x{:x} with status 0x{:x})",
|
||||
@@ -247,15 +56,16 @@ private:
|
||||
}
|
||||
|
||||
public:
|
||||
GreaseWeazleUsb(const std::string& port)
|
||||
GreaseWeazleUsb(const std::string& port):
|
||||
_serial(SerialPort::openSerialPort(port))
|
||||
{
|
||||
_fd = open_serial_port(port);
|
||||
|
||||
int version = getVersion();
|
||||
if (version == 22)
|
||||
_version = V22;
|
||||
if (version >= 29)
|
||||
_version = V29;
|
||||
else if (version >= 24)
|
||||
_version = V24;
|
||||
else if (version == 22)
|
||||
_version = V22;
|
||||
else
|
||||
{
|
||||
Error() << "only GreaseWeazle firmware versions 22 and 24 or above are currently "
|
||||
@@ -267,16 +77,11 @@ public:
|
||||
do_command({ CMD_SET_BUS_TYPE, 3, BUS_IBMPC });
|
||||
}
|
||||
|
||||
~GreaseWeazleUsb()
|
||||
{
|
||||
close_serial_port(_fd);
|
||||
}
|
||||
|
||||
int getVersion()
|
||||
{
|
||||
do_command({ CMD_GET_INFO, 3, GETINFO_FIRMWARE });
|
||||
|
||||
Bytes response = read_bytes(32);
|
||||
Bytes response = _serial->readBytes(32);
|
||||
ByteReader br(response);
|
||||
|
||||
br.seek(4);
|
||||
@@ -312,6 +117,7 @@ public:
|
||||
break;
|
||||
|
||||
case V24:
|
||||
case V29:
|
||||
{
|
||||
Bytes cmd(8);
|
||||
cmd.writer()
|
||||
@@ -328,13 +134,13 @@ public:
|
||||
uint32_t secondindex = ~0;
|
||||
for (;;)
|
||||
{
|
||||
uint8_t b = read_byte();
|
||||
uint8_t b = _serial->readByte();
|
||||
if (!b)
|
||||
break;
|
||||
|
||||
if (b == 255)
|
||||
{
|
||||
switch (read_byte())
|
||||
switch (_serial->readByte())
|
||||
{
|
||||
case FLUXOP_INDEX:
|
||||
{
|
||||
@@ -347,7 +153,7 @@ public:
|
||||
}
|
||||
|
||||
case FLUXOP_SPACE:
|
||||
read_bytes(4);
|
||||
_serial->readBytes(4);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -360,7 +166,7 @@ public:
|
||||
ticks_gw += b;
|
||||
else
|
||||
{
|
||||
int delta = 250 + (b-250)*255 + read_byte() - 1;
|
||||
int delta = 250 + (b-250)*255 + _serial->readByte() - 1;
|
||||
ticks_gw += delta;
|
||||
}
|
||||
}
|
||||
@@ -377,17 +183,37 @@ public:
|
||||
void testBulkWrite()
|
||||
{
|
||||
const int LEN = 10*1024*1024;
|
||||
Bytes cmd(6);
|
||||
ByteWriter bw(cmd);
|
||||
bw.write_8(CMD_SINK_BYTES);
|
||||
bw.write_8(cmd.size());
|
||||
bw.write_le32(LEN);
|
||||
Bytes cmd;
|
||||
switch (_version)
|
||||
{
|
||||
case V22:
|
||||
case V24:
|
||||
{
|
||||
cmd.resize(6);
|
||||
ByteWriter bw(cmd);
|
||||
bw.write_8(CMD_SINK_BYTES);
|
||||
bw.write_8(cmd.size());
|
||||
bw.write_le32(LEN);
|
||||
break;
|
||||
}
|
||||
|
||||
case V29:
|
||||
{
|
||||
cmd.resize(10);
|
||||
ByteWriter bw(cmd);
|
||||
bw.write_8(CMD_SINK_BYTES);
|
||||
bw.write_8(cmd.size());
|
||||
bw.write_le32(LEN);
|
||||
bw.write_le32(0); // seed
|
||||
break;
|
||||
}
|
||||
}
|
||||
do_command(cmd);
|
||||
|
||||
Bytes junk(LEN);
|
||||
double start_time = getCurrentTime();
|
||||
write_bytes(LEN);
|
||||
read_bytes(1);
|
||||
_serial->write(junk);
|
||||
_serial->readBytes(1);
|
||||
double elapsed_time = getCurrentTime() - start_time;
|
||||
|
||||
std::cout << "Transferred "
|
||||
@@ -403,15 +229,35 @@ public:
|
||||
void testBulkRead()
|
||||
{
|
||||
const int LEN = 10*1024*1024;
|
||||
Bytes cmd(6);
|
||||
ByteWriter bw(cmd);
|
||||
bw.write_8(CMD_SOURCE_BYTES);
|
||||
bw.write_8(cmd.size());
|
||||
bw.write_le32(LEN);
|
||||
Bytes cmd;
|
||||
switch (_version)
|
||||
{
|
||||
case V22:
|
||||
case V24:
|
||||
{
|
||||
cmd.resize(6);
|
||||
ByteWriter bw(cmd);
|
||||
bw.write_8(CMD_SOURCE_BYTES);
|
||||
bw.write_8(cmd.size());
|
||||
bw.write_le32(LEN);
|
||||
break;
|
||||
}
|
||||
|
||||
case V29:
|
||||
{
|
||||
cmd.resize(10);
|
||||
ByteWriter bw(cmd);
|
||||
bw.write_8(CMD_SOURCE_BYTES);
|
||||
bw.write_8(cmd.size());
|
||||
bw.write_le32(LEN);
|
||||
bw.write_le32(0); // seed
|
||||
break;
|
||||
}
|
||||
}
|
||||
do_command(cmd);
|
||||
|
||||
double start_time = getCurrentTime();
|
||||
read_bytes(LEN);
|
||||
_serial->readBytes(LEN);
|
||||
double elapsed_time = getCurrentTime() - start_time;
|
||||
|
||||
std::cout << "Transferred "
|
||||
@@ -447,6 +293,7 @@ public:
|
||||
}
|
||||
|
||||
case V24:
|
||||
case V29:
|
||||
{
|
||||
Bytes cmd(8);
|
||||
cmd.writer()
|
||||
@@ -462,7 +309,7 @@ public:
|
||||
ByteWriter bw(buffer);
|
||||
for (;;)
|
||||
{
|
||||
uint8_t b = read_byte();
|
||||
uint8_t b = _serial->readByte();
|
||||
if (!b)
|
||||
break;
|
||||
bw.write_8(b);
|
||||
@@ -489,11 +336,12 @@ public:
|
||||
break;
|
||||
|
||||
case V24:
|
||||
case V29:
|
||||
do_command({ CMD_WRITE_FLUX, 4, 1, 1 });
|
||||
break;
|
||||
}
|
||||
write_bytes(fluxEngineToGreaseWeazle(fldata, _clock));
|
||||
read_byte(); /* synchronise */
|
||||
_serial->write(fluxEngineToGreaseWeazle(fldata, _clock));
|
||||
_serial->readByte(); /* synchronise */
|
||||
|
||||
do_command({ CMD_GET_FLUX_STATUS, 2 });
|
||||
}
|
||||
@@ -511,7 +359,7 @@ public:
|
||||
bw.write_8(cmd.size());
|
||||
bw.write_le32(200e6 / _clock);
|
||||
do_command(cmd);
|
||||
read_byte(); /* synchronise */
|
||||
_serial->readByte(); /* synchronise */
|
||||
|
||||
do_command({ CMD_GET_FLUX_STATUS, 2 });
|
||||
}
|
||||
@@ -529,11 +377,12 @@ public:
|
||||
private:
|
||||
enum
|
||||
{
|
||||
V22,
|
||||
V24
|
||||
V22,
|
||||
V24,
|
||||
V29
|
||||
};
|
||||
|
||||
FileHandle _fd;
|
||||
std::unique_ptr<SerialPort> _serial;
|
||||
int _version;
|
||||
nanoseconds_t _clock;
|
||||
nanoseconds_t _revolutions;
|
||||
|
||||
225
lib/usb/serial.cc
Normal file
225
lib/usb/serial.cc
Normal file
@@ -0,0 +1,225 @@
|
||||
#include "globals.h"
|
||||
#include "usb.h"
|
||||
#include "protocol.h"
|
||||
#include "fluxmap.h"
|
||||
#include "bytes.h"
|
||||
#include "fmt/format.h"
|
||||
#include "serial.h"
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#if defined __WIN32__
|
||||
#include <windows.h>
|
||||
|
||||
class SerialPortImpl : public SerialPort
|
||||
{
|
||||
public:
|
||||
SerialPortImpl(const std::string& name)
|
||||
{
|
||||
_handle = CreateFileA(
|
||||
name.c_str(),
|
||||
/* dwDesiredAccess= */ GENERIC_READ|GENERIC_WRITE,
|
||||
/* dwShareMode= */ 0,
|
||||
/* lpSecurityAttribues= */ nullptr,
|
||||
/* dwCreationDisposition= */ OPEN_EXISTING,
|
||||
/* dwFlagsAndAttributes= */ FILE_ATTRIBUTE_NORMAL,
|
||||
/* hTemplateFile= */ nullptr);
|
||||
if (_handle == INVALID_HANDLE_VALUE)
|
||||
Error() << fmt::format("cannot open serial port '{}': {}",
|
||||
name, get_last_error_string());
|
||||
|
||||
DCB dcb =
|
||||
{
|
||||
.DCBlength = sizeof(DCB),
|
||||
.BaudRate = CBR_9600,
|
||||
.fBinary = true,
|
||||
.ByteSize = 8,
|
||||
.Parity = NOPARITY,
|
||||
.StopBits = ONESTOPBIT
|
||||
};
|
||||
SetCommState(_handle, &dcb);
|
||||
|
||||
COMMTIMEOUTS commtimeouts = {0};
|
||||
commtimeouts.ReadIntervalTimeout = 100;
|
||||
SetCommTimeouts(_handle, &commtimeouts);
|
||||
|
||||
PurgeComm(_handle, PURGE_RXABORT|PURGE_RXCLEAR|PURGE_TXABORT|PURGE_TXCLEAR);
|
||||
}
|
||||
|
||||
~SerialPortImpl() override
|
||||
{
|
||||
CloseHandle(_handle);
|
||||
}
|
||||
|
||||
public:
|
||||
ssize_t readImpl(uint8_t* buffer, size_t len) override
|
||||
{
|
||||
DWORD rlen;
|
||||
bool r = ReadFile(
|
||||
/* hFile= */ _handle,
|
||||
/* lpBuffer= */ buffer,
|
||||
/* nNumberOfBytesToRead= */ len,
|
||||
/* lpNumberOfBytesRead= */ &rlen,
|
||||
/* lpOverlapped= */ nullptr);
|
||||
if (!r)
|
||||
Error() << fmt::format("serial read I/O error: {}", get_last_error_string());
|
||||
return rlen;
|
||||
}
|
||||
|
||||
ssize_t write(const uint8_t* buffer, size_t len) override
|
||||
{
|
||||
DWORD wlen;
|
||||
/* Windows gets unhappy if we try to transfer too much... */
|
||||
len = std::min(4096U, len);
|
||||
bool r = WriteFile(
|
||||
/* hFile= */ _handle,
|
||||
/* lpBuffer= */ buffer,
|
||||
/* nNumberOfBytesToWrite= */ len,
|
||||
/* lpNumberOfBytesWritten= */ &wlen,
|
||||
/* lpOverlapped= */ nullptr);
|
||||
if (!r)
|
||||
Error() << fmt::format("serial write I/O error: {}", get_last_error_string());
|
||||
return wlen;
|
||||
}
|
||||
|
||||
private:
|
||||
static std::string get_last_error_string()
|
||||
{
|
||||
DWORD error = GetLastError();
|
||||
if (error == 0)
|
||||
return "OK";
|
||||
|
||||
LPSTR buffer = nullptr;
|
||||
size_t size = FormatMessageA(
|
||||
/* dwFlags= */ FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
/* lpSource= */ nullptr,
|
||||
/* dwMessageId= */ error,
|
||||
/* dwLanguageId= */ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
/* lpBuffer= */ (LPSTR) &buffer,
|
||||
/* nSize= */ 0,
|
||||
/* Arguments= */ nullptr);
|
||||
|
||||
std::string message(buffer, size);
|
||||
LocalFree(buffer);
|
||||
return message;
|
||||
}
|
||||
|
||||
private:
|
||||
HANDLE _handle;
|
||||
};
|
||||
|
||||
#else
|
||||
#include <termios.h>
|
||||
|
||||
class SerialPortImpl : public SerialPort
|
||||
{
|
||||
public:
|
||||
SerialPortImpl(const std::string& path)
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
if (path.find("/dev/tty.") != std::string::npos)
|
||||
std::cerr << "Warning: you probably want to be using a /dev/cu.* device\n";
|
||||
#endif
|
||||
|
||||
_fd = open(path.c_str(), O_RDWR);
|
||||
if (_fd == -1)
|
||||
Error() << fmt::format("cannot open serial port '{}': {}",
|
||||
path, strerror(errno));
|
||||
|
||||
struct termios t;
|
||||
tcgetattr(_fd, &t);
|
||||
t.c_iflag = 0;
|
||||
t.c_oflag = 0;
|
||||
t.c_cflag = CREAD;
|
||||
t.c_lflag = 0;
|
||||
t.c_cc[VMIN] = 1;
|
||||
cfsetspeed(&t, 9600);
|
||||
tcsetattr(_fd, TCSANOW, &t);
|
||||
}
|
||||
|
||||
~SerialPortImpl() override
|
||||
{
|
||||
close(_fd);
|
||||
}
|
||||
|
||||
public:
|
||||
ssize_t readImpl(uint8_t* buffer, size_t len) override
|
||||
{
|
||||
ssize_t rlen = ::read(_fd, buffer, len);
|
||||
if (rlen == -1)
|
||||
Error() << fmt::format("serial read I/O error: {}", strerror(errno));
|
||||
return rlen;
|
||||
}
|
||||
|
||||
ssize_t write(const uint8_t* buffer, size_t len) override
|
||||
{
|
||||
ssize_t wlen = ::write(_fd, buffer, len);
|
||||
if (wlen == -1)
|
||||
Error() << fmt::format("serial write I/O error: {}", strerror(errno));
|
||||
return wlen;
|
||||
}
|
||||
private:
|
||||
int _fd;
|
||||
};
|
||||
#endif
|
||||
|
||||
SerialPort::~SerialPort()
|
||||
{}
|
||||
|
||||
void SerialPort::read(uint8_t* buffer, size_t len)
|
||||
{
|
||||
while (len > 0)
|
||||
{
|
||||
if (_readbuffer_ptr < _readbuffer_fill)
|
||||
{
|
||||
int buffered = std::min(len, _readbuffer_fill - _readbuffer_ptr);
|
||||
memcpy(buffer, _readbuffer + _readbuffer_ptr, buffered);
|
||||
_readbuffer_ptr += buffered;
|
||||
buffer += buffered;
|
||||
len -= buffered;
|
||||
}
|
||||
|
||||
if (len == 0)
|
||||
break;
|
||||
|
||||
_readbuffer_fill = this->readImpl(_readbuffer, sizeof(_readbuffer));
|
||||
_readbuffer_ptr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void SerialPort::read(Bytes& bytes)
|
||||
{
|
||||
this->read(bytes.begin(), bytes.size());
|
||||
}
|
||||
|
||||
Bytes SerialPort::readBytes(size_t len)
|
||||
{
|
||||
Bytes b(len);
|
||||
this->read(b);
|
||||
return b;
|
||||
}
|
||||
|
||||
uint8_t SerialPort::readByte()
|
||||
{
|
||||
uint8_t b;
|
||||
this->read(&b, 1);
|
||||
return b;
|
||||
}
|
||||
|
||||
void SerialPort::write(const Bytes& bytes)
|
||||
{
|
||||
int ptr = 0;
|
||||
while (ptr < bytes.size())
|
||||
{
|
||||
ssize_t wlen = this->write(bytes.cbegin() + ptr, bytes.size() - ptr);
|
||||
ptr += wlen;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<SerialPort> SerialPort::openSerialPort(const std::string& path)
|
||||
{
|
||||
return std::make_unique<SerialPortImpl>(path);
|
||||
}
|
||||
|
||||
27
lib/usb/serial.h
Normal file
27
lib/usb/serial.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef SERIAL_H
|
||||
#define SERIAL_H
|
||||
|
||||
class SerialPort
|
||||
{
|
||||
public:
|
||||
static std::unique_ptr<SerialPort> openSerialPort(const std::string& path);
|
||||
|
||||
public:
|
||||
virtual ~SerialPort();
|
||||
virtual ssize_t readImpl(uint8_t* buffer, size_t len) = 0;
|
||||
virtual ssize_t write(const uint8_t* buffer, size_t len) = 0;
|
||||
|
||||
void read(uint8_t* buffer, size_t len);
|
||||
void read(Bytes& bytes);
|
||||
Bytes readBytes(size_t count);
|
||||
uint8_t readByte();
|
||||
void write(const Bytes& bytes);
|
||||
|
||||
private:
|
||||
uint8_t _readbuffer[4096];
|
||||
size_t _readbuffer_ptr = 0;
|
||||
size_t _readbuffer_fill = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
10
mkninja.sh
10
mkninja.sh
@@ -395,6 +395,7 @@ buildlibrary libbackend.a \
|
||||
lib/usb/fluxengineusb.cc \
|
||||
lib/usb/greaseweazle.cc \
|
||||
lib/usb/greaseweazleusb.cc \
|
||||
lib/usb/serial.cc \
|
||||
lib/usb/usb.cc \
|
||||
lib/usb/usbfinder.cc \
|
||||
lib/utils.cc \
|
||||
@@ -531,6 +532,9 @@ encodedecodetest atarist800
|
||||
encodedecodetest atarist820
|
||||
encodedecodetest brother120
|
||||
encodedecodetest brother240
|
||||
encodedecodetest commodore1541 scripts/commodore1541_test.textpb
|
||||
encodedecodetest commodore1581
|
||||
encodedecodetest hp9121
|
||||
encodedecodetest ibm1200_525
|
||||
encodedecodetest ibm1232
|
||||
encodedecodetest ibm1440
|
||||
@@ -538,12 +542,10 @@ encodedecodetest ibm180_525
|
||||
encodedecodetest ibm360_525
|
||||
encodedecodetest ibm720
|
||||
encodedecodetest ibm720_525
|
||||
encodedecodetest n88basic
|
||||
encodedecodetest tids990
|
||||
encodedecodetest commodore1581
|
||||
encodedecodetest commodore1541 scripts/commodore1541_test.textpb
|
||||
encodedecodetest mac400 scripts/mac400_test.textpb
|
||||
encodedecodetest mac800 scripts/mac800_test.textpb
|
||||
encodedecodetest n88basic
|
||||
encodedecodetest tids990
|
||||
encodedecodetest victor9k_ss
|
||||
|
||||
# vim: sw=4 ts=4 et
|
||||
|
||||
@@ -13,7 +13,9 @@ image_writer {
|
||||
|
||||
decoder {
|
||||
ibm {
|
||||
ignore_side_byte: true
|
||||
trackdata {
|
||||
ignore_side_byte: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,9 @@ image_writer {
|
||||
|
||||
decoder {
|
||||
ibm {
|
||||
ignore_side_byte: true
|
||||
trackdata {
|
||||
ignore_side_byte: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,14 @@ encoder {
|
||||
}
|
||||
|
||||
decoder {
|
||||
ibm {}
|
||||
ibm {
|
||||
trackdata {
|
||||
sector_range {
|
||||
min_sector: 1
|
||||
max_sector: 9
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cylinders {
|
||||
|
||||
@@ -55,7 +55,14 @@ encoder {
|
||||
}
|
||||
|
||||
decoder {
|
||||
ibm {}
|
||||
ibm {
|
||||
trackdata {
|
||||
sector_range {
|
||||
min_sector: 1
|
||||
max_sector: 9
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cylinders {
|
||||
|
||||
@@ -56,7 +56,14 @@ encoder {
|
||||
}
|
||||
|
||||
decoder {
|
||||
ibm {}
|
||||
ibm {
|
||||
trackdata {
|
||||
sector_range {
|
||||
min_sector: 1
|
||||
max_sector: 10
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cylinders {
|
||||
|
||||
@@ -56,7 +56,14 @@ encoder {
|
||||
}
|
||||
|
||||
decoder {
|
||||
ibm {}
|
||||
ibm {
|
||||
trackdata {
|
||||
sector_range {
|
||||
min_sector: 1
|
||||
max_sector: 10
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cylinders {
|
||||
|
||||
@@ -55,7 +55,14 @@ encoder {
|
||||
}
|
||||
|
||||
decoder {
|
||||
ibm {}
|
||||
ibm {
|
||||
trackdata {
|
||||
sector_range {
|
||||
min_sector: 1
|
||||
max_sector: 9
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cylinders {
|
||||
|
||||
@@ -55,7 +55,14 @@ encoder {
|
||||
}
|
||||
|
||||
decoder {
|
||||
ibm {}
|
||||
ibm {
|
||||
trackdata {
|
||||
sector_range {
|
||||
min_sector: 1
|
||||
max_sector: 9
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cylinders {
|
||||
|
||||
@@ -56,7 +56,14 @@ encoder {
|
||||
}
|
||||
|
||||
decoder {
|
||||
ibm {}
|
||||
ibm {
|
||||
trackdata {
|
||||
sector_range {
|
||||
min_sector: 1
|
||||
max_sector: 10
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cylinders {
|
||||
|
||||
@@ -56,7 +56,14 @@ encoder {
|
||||
}
|
||||
|
||||
decoder {
|
||||
ibm {}
|
||||
ibm {
|
||||
trackdata {
|
||||
sector_range {
|
||||
min_sector: 1
|
||||
max_sector: 10
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cylinders {
|
||||
|
||||
@@ -58,7 +58,13 @@ encoder {
|
||||
|
||||
decoder {
|
||||
ibm {
|
||||
swap_sides: false
|
||||
trackdata {
|
||||
swap_sides: false
|
||||
sector_range: {
|
||||
min_sector: 0
|
||||
max_sector: 9
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
comment: 'Hewlett-Packard 9121 270kB 3.5" SSDD'
|
||||
comment: 'Hewlett-Packard 9121 264kB 3.5" SSDD'
|
||||
|
||||
image_reader {
|
||||
filename: "hp9121.img"
|
||||
img {
|
||||
tracks: 70
|
||||
tracks: 66
|
||||
sides: 1
|
||||
trackdata {
|
||||
sector_size: 256
|
||||
sector_range {
|
||||
start_sector: 0
|
||||
sector_count: 18
|
||||
sector_count: 16
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,23 +27,32 @@ encoder {
|
||||
gap3: 44
|
||||
sectors {
|
||||
sector: 0
|
||||
sector: 1
|
||||
sector: 2
|
||||
sector: 3
|
||||
sector: 4
|
||||
sector: 5
|
||||
sector: 6
|
||||
sector: 7
|
||||
sector: 8
|
||||
sector: 9
|
||||
sector: 10
|
||||
sector: 11
|
||||
sector: 12
|
||||
sector: 1
|
||||
sector: 5
|
||||
sector: 9
|
||||
sector: 13
|
||||
sector: 2
|
||||
sector: 6
|
||||
sector: 10
|
||||
sector: 14
|
||||
sector: 3
|
||||
sector: 7
|
||||
sector: 11
|
||||
sector: 15
|
||||
sector: 16
|
||||
sector: 17
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
decoder {
|
||||
ibm {
|
||||
trackdata {
|
||||
sector_range {
|
||||
min_sector: 0
|
||||
max_sector: 15
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,14 @@ encoder {
|
||||
}
|
||||
|
||||
decoder {
|
||||
ibm {}
|
||||
ibm {
|
||||
trackdata {
|
||||
sector_range {
|
||||
min_sector: 1
|
||||
max_sector: 15
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cylinders {
|
||||
|
||||
@@ -53,7 +53,14 @@ encoder {
|
||||
}
|
||||
|
||||
decoder {
|
||||
ibm {}
|
||||
ibm {
|
||||
trackdata {
|
||||
sector_range {
|
||||
min_sector: 1
|
||||
max_sector: 8
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cylinders {
|
||||
|
||||
@@ -50,7 +50,14 @@ encoder {
|
||||
}
|
||||
|
||||
decoder {
|
||||
ibm {}
|
||||
ibm {
|
||||
trackdata {
|
||||
sector_range {
|
||||
min_sector: 1
|
||||
max_sector: 18
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cylinders {
|
||||
|
||||
@@ -42,7 +42,14 @@ encoder {
|
||||
}
|
||||
|
||||
decoder {
|
||||
ibm {}
|
||||
ibm {
|
||||
trackdata {
|
||||
sector_range {
|
||||
min_sector: 1
|
||||
max_sector: 9
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cylinders {
|
||||
|
||||
@@ -42,7 +42,14 @@ encoder {
|
||||
}
|
||||
|
||||
decoder {
|
||||
ibm {}
|
||||
ibm {
|
||||
trackdata {
|
||||
sector_range {
|
||||
min_sector: 1
|
||||
max_sector: 9
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cylinders {
|
||||
|
||||
@@ -41,7 +41,14 @@ encoder {
|
||||
}
|
||||
|
||||
decoder {
|
||||
ibm {}
|
||||
ibm {
|
||||
trackdata {
|
||||
sector_range {
|
||||
min_sector: 1
|
||||
max_sector: 9
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cylinders {
|
||||
|
||||
@@ -41,7 +41,14 @@ encoder {
|
||||
}
|
||||
|
||||
decoder {
|
||||
ibm {}
|
||||
ibm {
|
||||
trackdata {
|
||||
sector_range {
|
||||
min_sector: 1
|
||||
max_sector: 9
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cylinders {
|
||||
|
||||
@@ -120,7 +120,14 @@ encoder {
|
||||
}
|
||||
|
||||
decoder {
|
||||
ibm {}
|
||||
ibm {
|
||||
trackdata {
|
||||
sector_range {
|
||||
min_sector: 1
|
||||
max_sector: 26
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cylinders {
|
||||
|
||||
Reference in New Issue
Block a user