Merge from master.

This commit is contained in:
David Given
2021-12-12 19:49:32 +00:00
27 changed files with 598 additions and 287 deletions

View File

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