scpi: Do not block when reading header of definite length blocks

When using SCPI over serial (over USB), we want the header without waiting
for the terminating newline, as otherwise the transfer may time out.
sr_scpi_get_data() will block until the message is complete.
This commit is contained in:
Stefan Brüns 2017-01-13 23:10:41 +01:00 committed by Uwe Hermann
parent 9ea4018f2f
commit d64be25be4
2 changed files with 82 additions and 31 deletions

View File

@ -120,6 +120,8 @@ SR_PRIV int sr_scpi_read_complete(struct sr_scpi_dev_inst *scpi);
SR_PRIV int sr_scpi_close(struct sr_scpi_dev_inst *scpi); SR_PRIV int sr_scpi_close(struct sr_scpi_dev_inst *scpi);
SR_PRIV void sr_scpi_free(struct sr_scpi_dev_inst *scpi); SR_PRIV void sr_scpi_free(struct sr_scpi_dev_inst *scpi);
SR_PRIV int sr_scpi_read_response(struct sr_scpi_dev_inst *scpi,
GString *response, gint64 abs_timeout_us);
SR_PRIV int sr_scpi_get_string(struct sr_scpi_dev_inst *scpi, SR_PRIV int sr_scpi_get_string(struct sr_scpi_dev_inst *scpi,
const char *command, char **scpi_response); const char *command, char **scpi_response);
SR_PRIV int sr_scpi_get_bool(struct sr_scpi_dev_inst *scpi, SR_PRIV int sr_scpi_get_bool(struct sr_scpi_dev_inst *scpi,

View File

@ -412,14 +412,49 @@ SR_PRIV int sr_scpi_get_string(struct sr_scpi_dev_inst *scpi,
return SR_OK; return SR_OK;
} }
/**
* Do a non-blocking read of up to the allocated length, and
* check if a timeout has occured.
*
* @param scpi Previously initialised SCPI device structure.
* @param response Buffer to which the response is appended.
* @param abs_timeout_us Absolute timeout in microseconds
*
* @return read length on success, SR_ERR* on failure.
*/
SR_PRIV int sr_scpi_read_response(struct sr_scpi_dev_inst *scpi,
GString *response, gint64 abs_timeout_us)
{
int len, space;
space = response->allocated_len - response->len;
len = sr_scpi_read_data(scpi, &response->str[response->len], space);
if (len < 0) {
sr_err("Incompletely read SCPI response.");
return SR_ERR;
}
if (len > 0) {
g_string_set_size(response, response->len + len);
return len;
}
if (g_get_monotonic_time() > abs_timeout_us) {
sr_err("Timed out waiting for SCPI response.");
return SR_ERR_TIMEOUT;
}
return 0;
}
SR_PRIV int sr_scpi_get_data(struct sr_scpi_dev_inst *scpi, SR_PRIV int sr_scpi_get_data(struct sr_scpi_dev_inst *scpi,
const char *command, GString **scpi_response) const char *command, GString **scpi_response)
{ {
int len; int ret;
GString *response; GString *response;
gint64 laststart, now;
unsigned int offset;
int space; int space;
gint64 timeout;
/* Optionally send caller provided command. */ /* Optionally send caller provided command. */
if (command) { if (command) {
@ -432,37 +467,26 @@ SR_PRIV int sr_scpi_get_data(struct sr_scpi_dev_inst *scpi,
return SR_ERR; return SR_ERR;
/* Keep reading until completion or until timeout. */ /* Keep reading until completion or until timeout. */
laststart = g_get_monotonic_time(); timeout = g_get_monotonic_time() + scpi->read_timeout_us;
response = *scpi_response; response = *scpi_response;
offset = response->len;
while (!sr_scpi_read_complete(scpi)) { while (!sr_scpi_read_complete(scpi)) {
/* Resize the buffer when free space drops below a threshold. */ /* Resize the buffer when free space drops below a threshold. */
space = response->allocated_len - response->len; space = response->allocated_len - response->len;
if (space < 128) { if (space < 128) {
g_string_set_size(response, response->len + 1024); int oldlen = response->len;
g_string_set_size(response, offset); g_string_set_size(response, oldlen + 1024);
space = response->allocated_len - response->len; g_string_set_size(response, oldlen);
} }
/* Read another chunk of the response. */ /* Read another chunk of the response. */
len = sr_scpi_read_data(scpi, &response->str[offset], space); ret = sr_scpi_read_response(scpi, response, timeout);
if (len < 0) {
sr_err("Incompletely read SCPI response.");
return SR_ERR;
}
now = g_get_monotonic_time(); if (ret < 0)
return ret;
if (len > 0) { if (ret > 0)
laststart = now; timeout = g_get_monotonic_time() + scpi->read_timeout_us;
offset += len;
g_string_set_size(response, offset);
} else if ((now - laststart) >= scpi->read_timeout_us) {
/* Quit reading after a period of time without receiving data. */
sr_err("Timed out waiting for SCPI response.");
return SR_ERR_TIMEOUT;
}
} }
return SR_OK; return SR_OK;
@ -753,19 +777,32 @@ SR_PRIV int sr_scpi_get_block(struct sr_scpi_dev_inst *scpi,
char buf[10]; char buf[10];
long llen; long llen;
long datalen; long datalen;
gint64 timeout;
if (command)
if (sr_scpi_send(scpi, command) != SR_OK)
return SR_ERR;
if (sr_scpi_read_begin(scpi) != SR_OK)
return SR_ERR;
/* /*
* Assume an initial maximum length, optionally gets adjusted below. * Assume an initial maximum length, optionally gets adjusted below.
* Prepare a NULL return value for when error paths will be taken. * Prepare a NULL return value for when error paths will be taken.
*/ */
response = g_string_sized_new(1024); response = g_string_sized_new(1024);
timeout = g_get_monotonic_time() + scpi->read_timeout_us;
*scpi_response = NULL; *scpi_response = NULL;
/* Get (the first chunk of) the response. */ /* Get (the first chunk of) the response. */
ret = sr_scpi_get_data(scpi, command, &response); while (response->len < 2) {
if (ret != SR_OK) { ret = sr_scpi_read_response(scpi, response, timeout);
g_string_free(response, TRUE); if (ret < 0) {
return ret; g_string_free(response, TRUE);
return ret;
}
} }
/* /*
@ -790,6 +827,15 @@ SR_PRIV int sr_scpi_get_block(struct sr_scpi_dev_inst *scpi,
g_string_free(response, TRUE); g_string_free(response, TRUE);
return ret; return ret;
} }
while (response->len < (unsigned long)(2 + llen)) {
ret = sr_scpi_read_response(scpi, response, timeout);
if (ret < 0) {
g_string_free(response, TRUE);
return ret;
}
}
memcpy(buf, &response->str[2], llen); memcpy(buf, &response->str[2], llen);
buf[llen] = '\0'; buf[llen] = '\0';
ret = sr_atol(buf, &datalen); ret = sr_atol(buf, &datalen);
@ -809,19 +855,22 @@ SR_PRIV int sr_scpi_get_block(struct sr_scpi_dev_inst *scpi,
g_string_set_size(response, datalen); g_string_set_size(response, datalen);
g_string_set_size(response, oldlen); g_string_set_size(response, oldlen);
} }
while (response->len < (unsigned long)(datalen)) { while (response->len < (unsigned long)(datalen)) {
ret = sr_scpi_get_data(scpi, NULL, &response); ret = sr_scpi_read_response(scpi, response, timeout);
if (ret != SR_OK) { if (ret < 0) {
g_string_free(response, TRUE); g_string_free(response, TRUE);
return ret; return ret;
} }
if (ret > 0)
timeout = g_get_monotonic_time() + scpi->read_timeout_us;
} }
/* Convert received data to byte array. */ /* Convert received data to byte array. */
*scpi_response = g_byte_array_new_take( *scpi_response = g_byte_array_new_take(
(guint8*)g_string_free(response, FALSE), datalen); (guint8*)g_string_free(response, FALSE), datalen);
return ret; return SR_OK;
} }
/** /**