Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactored SCPITMCTransport #950

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 44 additions & 120 deletions scopehal/SCPITMCTransport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,60 +49,39 @@ using namespace std;

SCPITMCTransport::SCPITMCTransport(const string& args)
: m_devicePath(args)
, m_staging_buf_size(0)
, m_staging_buf(nullptr)
, m_data_in_staging_buf(0)
, m_data_offset(0)
, m_data_depleted(false)
, m_fix_buggy_driver(false)
, m_transfer_size(48)
, m_buf(nullptr)
, m_max_read_size(2032)
{
// TODO: add configuration options:
// - set the maximum request size of usbtmc read requests (currently 2032)
// - set timeout value (when using kernel that has usbtmc v2 version)
// - set size of staging buffer (or get rid of it entirely?)
// <path>[:<max_read_size>]

// FIXME: currently not used
m_timeout = 1000;
char *dev = strdup(m_devicePath.c_str());

LogDebug("Connecting to SCPI oscilloscope over USBTMC through %s\n", m_devicePath.c_str());

char devicePath[128] = "";
char transferBufferSize[128] = "";
char *ptr;
if(2 == sscanf(m_devicePath.c_str(), "%127[^:]:%127s", devicePath, transferBufferSize))
{
// set size for a buggy tmc firmware
// FIXME: Workaround for Siglent SDS1x04X-E. Max request size is 48 byte. Bug in firmware 8.2.6.1.37R9 ?
m_fix_buggy_driver = true;
m_transfer_size = strtol(transferBufferSize, &ptr, 10);
if(m_transfer_size == 0)
{
LogNotice("USBTMC wrong size value %s\n", transferBufferSize);
return;
}
LogNotice("Set USBTMC transfer size %d bytes. Workaround for buggy firmware.\n", m_transfer_size);
char *colon = strchr(dev, ':');
if (colon) {
m_max_read_size = atoi(colon + 1);
*colon = '\0';
}

m_handle = open(devicePath, O_RDWR);
LogDebug("Connecting to SCPI oscilloscope over USBTMC through %s, max read size %d\n", dev, (int)m_max_read_size);

m_handle = open(dev, O_RDWR);
if (m_handle <= 0)
{
LogError("Couldn't open %s\n", devicePath);
LogError("Couldn't open %s\n", dev);
return;
}

// Right now, I'm using an internal staging buffer because this code has been copied from the lxi transport driver.
// It's not strictly needed...
m_staging_buf_size = 150000000;
m_staging_buf = new unsigned char[m_staging_buf_size];
free(dev);

m_buf = new unsigned char[m_max_read_size+1];
}

SCPITMCTransport::~SCPITMCTransport()
{
if (IsConnected())
close(m_handle);

delete[] m_staging_buf;
delete[] m_buf;
}

bool SCPITMCTransport::IsConnected()
Expand All @@ -129,35 +108,36 @@ bool SCPITMCTransport::SendCommand(const string& cmd)
return false;

LogTrace("Sending %s\n", cmd.c_str());

int result = write(m_handle, cmd.c_str(), cmd.length());

m_data_in_staging_buf = 0;
m_data_offset = 0;
m_data_depleted = false;

return (result == (int)cmd.length());
}

string SCPITMCTransport::ReadReply(bool endOnSemicolon, [[maybe_unused]] function<void(float)> progress)
{
string ret;

if (!m_staging_buf || !IsConnected())
if (!m_buf || !IsConnected())
return ret;

//FIXME: there *has* to be a more efficient way to do this...
char tmp = ' ';
while(true)
{
if (m_data_depleted)
bool done = false;
while (!done) {
int r = read(m_handle, (char *)m_buf, m_max_read_size);
if (r <= 0) {
LogError("Read error\n");
break;
ReadRawData(1, (unsigned char *)&tmp);
if( (tmp == '\n') || ( (tmp == ';') && endOnSemicolon ) )
break;
else
ret += tmp;
}
m_buf[r] = '\0';
for (int i=0; i<r; i++) {
char c = m_buf[i];
if (c == '\n' || c == '\r' || (c == ';' && endOnSemicolon)) {
done = true;
break;
} else {
ret += c;
}
}
}

LogTrace("Got %s\n", ret.c_str());
return ret;
}
Expand All @@ -170,73 +150,17 @@ void SCPITMCTransport::SendRawData(size_t len, const unsigned char* buf)

size_t SCPITMCTransport::ReadRawData(size_t len, unsigned char* buf, std::function<void(float)> /*progress*/)
{
// Data in the staging buffer is assumed to always be a consequence of a SendCommand request.
// Since we fetch all the reply data in one go, once all this data has been fetched, we mark
// the staging buffer as depleted and don't issue a new read until a new SendCommand
// is issued.

if (!m_staging_buf || !IsConnected())
return 0;

if (!m_data_depleted)
{
if (m_data_in_staging_buf == 0)
{

#if 0
// This is what we'd use if we could be sure that the installed Linux kernel had
// usbtmc driver v2.
m_data_in_staging_buf = read(m_handle, (char *)m_staging_buf, m_staging_buf_size);
#else
// Split up one potentially large read into a bunch of smaller ones.
// The performance impact of this is pretty small.
const int max_bytes_per_req = 2032;
int i = 0;
int bytes_fetched, bytes_requested;

do
{
if(m_fix_buggy_driver == false)
{
bytes_requested = (max_bytes_per_req < len) ? max_bytes_per_req : len;
bytes_fetched = read(m_handle, (char *)m_staging_buf + i, m_staging_buf_size);
}
else
{
// limit each request to m_transfer_size
bytes_requested = m_transfer_size;
bytes_fetched = read(m_handle, (char *)m_staging_buf + i, m_transfer_size);
}
i += bytes_fetched;
} while(bytes_fetched == bytes_requested);

m_data_in_staging_buf = i;
#endif

if (m_data_in_staging_buf <= 0)
m_data_in_staging_buf = 0;
m_data_offset = 0;
}

unsigned int data_left = m_data_in_staging_buf - m_data_offset;
if (data_left > 0)
{
int nr_bytes = len > data_left ? data_left : len;

memcpy(buf, m_staging_buf + m_data_offset, nr_bytes);

m_data_offset += nr_bytes;
size_t done = 0;
while (done < len) {
size_t todo = len - done;
if (todo > m_max_read_size)
todo = m_max_read_size;
int r = read(m_handle, buf + done, todo);
if (r <= 0) {
LogError("Read error\n");
break;
}

if (m_data_offset == m_data_in_staging_buf)
m_data_depleted = true;
}
else
{
// When this happens, the SCPIDevice is fetching more data from device than what
// could be expected from the SendCommand that was issued.
LogDebug("ReadRawData: data depleted.\n");
return 0;
done += r;
}

return len;
Expand Down
11 changes: 2 additions & 9 deletions scopehal/SCPITMCTransport.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,8 @@ class SCPITMCTransport : public SCPITransport
std::string m_devicePath;

int m_handle;
int m_timeout;

int m_staging_buf_size;
unsigned char *m_staging_buf;
int m_data_in_staging_buf;
int m_data_offset;
bool m_data_depleted;
bool m_fix_buggy_driver;
int m_transfer_size;
unsigned char *m_buf;
size_t m_max_read_size;
};

#endif