From: Frank Stettner Date: Sat, 11 Nov 2017 15:29:55 +0000 (+0100) Subject: scpi: Synchronize read, write and write+read operations. X-Git-Url: http://sigrok.org/gitweb/?p=libsigrok.git;a=commitdiff_plain;h=fd20e59caa4b2b05ddbca55f77ff8b1cf8ae0446 scpi: Synchronize read, write and write+read operations. This ensures that SCPI read/write/write+read operations are thread safe. F.e.: If a write operation expects a return value (in other words: a read operation), it is not allowed to be interrupted by another write operation. To simplify things, the SCPI helper functions are moved from scpi/helpers.c to scpi/scpi.c and also are renamed to fit the naming scheme. libgpib in particular will abort the program execution in case of concurrent operations. --- diff --git a/Makefile.am b/Makefile.am index 7aecd33c..cfa5502e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -108,7 +108,6 @@ libsigrok_la_SOURCES += \ libsigrok_la_SOURCES += \ src/scpi.h \ src/scpi/scpi.c \ - src/scpi/helpers.c \ src/scpi/scpi_tcp.c if NEED_RPC libsigrok_la_SOURCES += \ diff --git a/src/scpi.h b/src/scpi.h index 93230062..6fc99f7d 100644 --- a/src/scpi.h +++ b/src/scpi.h @@ -99,6 +99,7 @@ struct sr_scpi_dev_inst { void *priv; /* Only used for quirk workarounds, notably the Rigol DS1000 series. */ uint64_t firmware_version; + GMutex scpi_mutex; }; SR_PRIV GSList *sr_scpi_scan(struct drv_context *drvc, GSList *options, @@ -148,10 +149,10 @@ SR_PRIV int sr_scpi_get_hw_id(struct sr_scpi_dev_inst *scpi, SR_PRIV void sr_scpi_hw_info_free(struct sr_scpi_hw_info *hw_info); SR_PRIV const char *sr_vendor_alias(const char *raw_vendor); -SR_PRIV const char *scpi_cmd_get(const struct scpi_command *cmdtable, int command); -SR_PRIV int scpi_cmd(const struct sr_dev_inst *sdi, +SR_PRIV const char *sr_scpi_cmd_get(const struct scpi_command *cmdtable, int command); +SR_PRIV int sr_scpi_cmd(const struct sr_dev_inst *sdi, const struct scpi_command *cmdtable, int command, ...); -SR_PRIV int scpi_cmd_resp(const struct sr_dev_inst *sdi, +SR_PRIV int sr_scpi_cmd_resp(const struct sr_dev_inst *sdi, const struct scpi_command *cmdtable, GVariant **gvar, const GVariantType *gvtype, int command, ...); diff --git a/src/scpi/helpers.c b/src/scpi/helpers.c deleted file mode 100644 index dc19c3ab..00000000 --- a/src/scpi/helpers.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * This file is part of the libsigrok project. - * - * Copyright (C) 2015 Bert Vermeulen - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include "libsigrok-internal.h" -#include "scpi.h" - -#define LOG_PREFIX "scpi/helpers" - -static const char *scpi_vendors[][2] = { - { "HEWLETT-PACKARD", "HP" }, - { "Agilent Technologies", "Agilent" }, - { "RIGOL TECHNOLOGIES", "Rigol" }, - { "PHILIPS", "Philips" }, - { "CHROMA", "Chroma" }, - { "Chroma ATE", "Chroma" }, -}; - -SR_PRIV const char *sr_vendor_alias(const char *raw_vendor) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(scpi_vendors); i++) { - if (!g_ascii_strcasecmp(raw_vendor, scpi_vendors[i][0])) - return scpi_vendors[i][1]; - } - - return raw_vendor; -} - -SR_PRIV const char *scpi_cmd_get(const struct scpi_command *cmdtable, int command) -{ - unsigned int i; - const char *cmd; - - if (!cmdtable) - return NULL; - - cmd = NULL; - for (i = 0; cmdtable[i].string; i++) { - if (cmdtable[i].command == command) { - cmd = cmdtable[i].string; - break; - } - } - - return cmd; -} - -SR_PRIV int scpi_cmd(const struct sr_dev_inst *sdi, const struct scpi_command *cmdtable, - int command, ...) -{ - struct sr_scpi_dev_inst *scpi; - va_list args; - int ret; - const char *cmd; - - if (!(cmd = scpi_cmd_get(cmdtable, command))) { - /* Device does not implement this command, that's OK. */ - return SR_OK; - } - - scpi = sdi->conn; - va_start(args, command); - ret = sr_scpi_send_variadic(scpi, cmd, args); - va_end(args); - - return ret; -} - -SR_PRIV int scpi_cmd_resp(const struct sr_dev_inst *sdi, const struct scpi_command *cmdtable, - GVariant **gvar, const GVariantType *gvtype, int command, ...) -{ - struct sr_scpi_dev_inst *scpi; - va_list args; - double d; - int ret; - char *s; - const char *cmd; - - if (!(cmd = scpi_cmd_get(cmdtable, command))) { - /* Device does not implement this command. */ - return SR_ERR_NA; - } - - scpi = sdi->conn; - va_start(args, command); - ret = sr_scpi_send_variadic(scpi, cmd, args); - va_end(args); - if (ret != SR_OK) - return ret; - - /* Straight SCPI getters to GVariant types. */ - if (g_variant_type_equal(gvtype, G_VARIANT_TYPE_BOOLEAN)) { - if ((ret = sr_scpi_get_string(scpi, NULL, &s)) != SR_OK) - return ret; - if (!g_ascii_strcasecmp(s, "ON") || !g_ascii_strcasecmp(s, "1") - || !g_ascii_strcasecmp(s, "YES")) - *gvar = g_variant_new_boolean(TRUE); - else if (!g_ascii_strcasecmp(s, "OFF") || !g_ascii_strcasecmp(s, "0") - || !g_ascii_strcasecmp(s, "NO")) - *gvar = g_variant_new_boolean(FALSE); - else - ret = SR_ERR; - g_free(s); - } else if (g_variant_type_equal(gvtype, G_VARIANT_TYPE_DOUBLE)) { - if ((ret = sr_scpi_get_double(scpi, NULL, &d)) == SR_OK) - *gvar = g_variant_new_double(d); - } else if (g_variant_type_equal(gvtype, G_VARIANT_TYPE_STRING)) { - if ((ret = sr_scpi_get_string(scpi, NULL, &s)) == SR_OK) - *gvar = g_variant_new_string(s); - } else { - sr_err("Unable to convert to desired GVariant type."); - ret = SR_ERR_NA; - } - - return ret; -} diff --git a/src/scpi/scpi.c b/src/scpi/scpi.c index 3601ea55..8d05235c 100644 --- a/src/scpi/scpi.c +++ b/src/scpi/scpi.c @@ -2,6 +2,7 @@ * This file is part of the libsigrok project. * * Copyright (C) 2013 poljar (Damir Jelić) + * Copyright (C) 2015 Bert Vermeulen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,6 +30,15 @@ #define SCPI_READ_RETRIES 100 #define SCPI_READ_RETRY_TIMEOUT_US (10 * 1000) +static const char *scpi_vendors[][2] = { + { "HEWLETT-PACKARD", "HP" }, + { "Agilent Technologies", "Agilent" }, + { "RIGOL TECHNOLOGIES", "Rigol" }, + { "PHILIPS", "Philips" }, + { "CHROMA", "Chroma" }, + { "Chroma ATE", "Chroma" }, +}; + /** * Parse a string representation of a boolean-like value into a gboolean. * Similar to sr_parse_boolstring but rejects strings which do not represent @@ -122,6 +132,178 @@ static struct sr_dev_inst *sr_scpi_scan_resource(struct drv_context *drvc, return sdi; } +/** + * Send a SCPI command with a variadic argument list without mutex. + * + * @param scpi Previously initialized SCPI device structure. + * @param format Format string. + * @param args Argument list. + * + * @return SR_OK on success, SR_ERR on failure. + */ +static int scpi_send_variadic(struct sr_scpi_dev_inst *scpi, + const char *format, va_list args) +{ + va_list args_copy; + char *buf; + int len, ret; + + /* Get length of buffer required. */ + va_copy(args_copy, args); + len = vsnprintf(NULL, 0, format, args_copy); + va_end(args_copy); + + /* Allocate buffer and write out command. */ + buf = g_malloc0(len + 2); + vsprintf(buf, format, args); + if (buf[len - 1] != '\n') + buf[len] = '\n'; + + /* Send command. */ + ret = scpi->send(scpi->priv, buf); + + /* Free command buffer. */ + g_free(buf); + + return ret; +} + +/** + * Send a SCPI command without mutex. + * + * @param scpi Previously initialized SCPI device structure. + * @param format Format string, to be followed by any necessary arguments. + * + * @return SR_OK on success, SR_ERR on failure. + */ +static int scpi_send(struct sr_scpi_dev_inst *scpi, const char *format, + va_list args) +{ + return scpi_send_variadic(scpi, format, args); +} + +/** + * Send data to SCPI device without mutex. + * + * TODO: This is only implemented in TcpRaw, but never used. + * TODO: Use Mutex at all? + * + * @param scpi Previously initialised SCPI device structure. + * @param buf Buffer with data to send. + * @param len Number of bytes to send. + * + * @return Number of bytes read, or SR_ERR upon failure. + */ +static int scpi_write_data(struct sr_scpi_dev_inst *scpi, char *buf, int maxlen) +{ + return scpi->write_data(scpi->priv, buf, maxlen); +} + +/** + * Read part of a response from SCPI device without mutex. + * + * @param scpi Previously initialised SCPI device structure. + * @param buf Buffer to store result. + * @param maxlen Maximum number of bytes to read. + * + * @return Number of bytes read, or SR_ERR upon failure. + */ +static int scpi_read_data(struct sr_scpi_dev_inst *scpi, char *buf, int maxlen) +{ + return scpi->read_data(scpi->priv, buf, maxlen); +} + +/** + * Do a non-blocking read of up to the allocated length, and + * check if a timeout has occured, without mutex. + * + * @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. + */ +static int 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 = scpi->read_data(scpi->priv, &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; +} + +/** + * Send a SCPI command, receive the reply and store the reply in + * scpi_response, without mutex. + * + * @param scpi Previously initialised SCPI device structure. + * @param command The SCPI command to send to the device. + * @param scpi_response Pointer where to store the SCPI response. + * + * @return SR_OK on success, SR_ERR on failure. + */ +static int scpi_get_data(struct sr_scpi_dev_inst *scpi, + const char *command, GString **scpi_response) +{ + int ret; + GString *response; + int space; + gint64 timeout; + va_list empty_va_list; + + /* Optionally send caller provided command. */ + if (command) { + if (scpi_send(scpi, command, empty_va_list) != SR_OK) + return SR_ERR; + } + + /* Initiate SCPI read operation. */ + if (sr_scpi_read_begin(scpi) != SR_OK) + return SR_ERR; + + /* Keep reading until completion or until timeout. */ + timeout = g_get_monotonic_time() + scpi->read_timeout_us; + + response = *scpi_response; + + while (!sr_scpi_read_complete(scpi)) { + /* Resize the buffer when free space drops below a threshold. */ + space = response->allocated_len - response->len; + if (space < 128) { + int oldlen = response->len; + g_string_set_size(response, oldlen + 1024); + g_string_set_size(response, oldlen); + } + + /* Read another chunk of the response. */ + ret = scpi_read_response(scpi, response, timeout); + + if (ret < 0) + return ret; + if (ret > 0) + timeout = g_get_monotonic_time() + scpi->read_timeout_us; + } + + return SR_OK; +} + SR_PRIV GSList *sr_scpi_scan(struct drv_context *drvc, GSList *options, struct sr_dev_inst *(*probe_device)(struct sr_scpi_dev_inst *scpi)) { @@ -214,6 +396,8 @@ SR_PRIV struct sr_scpi_dev_inst *scpi_dev_inst_new(struct drv_context *drvc, */ SR_PRIV int sr_scpi_open(struct sr_scpi_dev_inst *scpi) { + g_mutex_init(&scpi->scpi_mutex); + return scpi->open(scpi); } @@ -268,7 +452,9 @@ SR_PRIV int sr_scpi_send(struct sr_scpi_dev_inst *scpi, int ret; va_start(args, format); - ret = sr_scpi_send_variadic(scpi, format, args); + g_mutex_lock(&scpi->scpi_mutex); + ret = scpi_send_variadic(scpi, format, args); + g_mutex_unlock(&scpi->scpi_mutex); va_end(args); return ret; @@ -286,26 +472,11 @@ SR_PRIV int sr_scpi_send(struct sr_scpi_dev_inst *scpi, SR_PRIV int sr_scpi_send_variadic(struct sr_scpi_dev_inst *scpi, const char *format, va_list args) { - va_list args_copy; - char *buf; - int len, ret; - - /* Get length of buffer required. */ - va_copy(args_copy, args); - len = vsnprintf(NULL, 0, format, args_copy); - va_end(args_copy); - - /* Allocate buffer and write out command. */ - buf = g_malloc0(len + 2); - vsprintf(buf, format, args); - if (buf[len - 1] != '\n') - buf[len] = '\n'; - - /* Send command. */ - ret = scpi->send(scpi->priv, buf); + int ret; - /* Free command buffer. */ - g_free(buf); + g_mutex_lock(&scpi->scpi_mutex); + ret = scpi_send_variadic(scpi, format, args); + g_mutex_unlock(&scpi->scpi_mutex); return ret; } @@ -334,12 +505,21 @@ SR_PRIV int sr_scpi_read_begin(struct sr_scpi_dev_inst *scpi) SR_PRIV int sr_scpi_read_data(struct sr_scpi_dev_inst *scpi, char *buf, int maxlen) { - return scpi->read_data(scpi->priv, buf, maxlen); + int ret; + + g_mutex_lock(&scpi->scpi_mutex); + ret = scpi_read_data(scpi, buf, maxlen); + g_mutex_unlock(&scpi->scpi_mutex); + + return ret; } /** * Send data to SCPI device. * + * TODO: This is only implemented in TcpRaw, but never used. + * TODO: Use Mutex at all? + * * @param scpi Previously initialised SCPI device structure. * @param buf Buffer with data to send. * @param len Number of bytes to send. @@ -349,7 +529,13 @@ SR_PRIV int sr_scpi_read_data(struct sr_scpi_dev_inst *scpi, SR_PRIV int sr_scpi_write_data(struct sr_scpi_dev_inst *scpi, char *buf, int maxlen) { - return scpi->write_data(scpi->priv, buf, maxlen); + int ret; + + g_mutex_lock(&scpi->scpi_mutex); + ret = scpi_write_data(scpi, buf, maxlen); + g_mutex_unlock(&scpi->scpi_mutex); + + return ret; } /** @@ -373,7 +559,14 @@ 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) { - return scpi->close(scpi); + int ret; + + g_mutex_lock(&scpi->scpi_mutex); + ret = scpi->close(scpi); + g_mutex_unlock(&scpi->scpi_mutex); + g_mutex_clear(&scpi->scpi_mutex); + + return ret; } /** @@ -442,71 +635,25 @@ SR_PRIV int sr_scpi_get_string(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) { - 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; - } + int ret; - if (g_get_monotonic_time() > abs_timeout_us) { - sr_err("Timed out waiting for SCPI response."); - return SR_ERR_TIMEOUT; - } + g_mutex_lock(&scpi->scpi_mutex); + ret = scpi_read_response(scpi, response, abs_timeout_us); + g_mutex_unlock(&scpi->scpi_mutex); - return 0; + return ret; } SR_PRIV int sr_scpi_get_data(struct sr_scpi_dev_inst *scpi, const char *command, GString **scpi_response) { int ret; - GString *response; - int space; - gint64 timeout; - - /* Optionally send caller provided command. */ - if (command) { - if (sr_scpi_send(scpi, command) != SR_OK) - return SR_ERR; - } - - /* Initiate SCPI read operation. */ - if (sr_scpi_read_begin(scpi) != SR_OK) - return SR_ERR; - - /* Keep reading until completion or until timeout. */ - timeout = g_get_monotonic_time() + scpi->read_timeout_us; - - response = *scpi_response; - - while (!sr_scpi_read_complete(scpi)) { - /* Resize the buffer when free space drops below a threshold. */ - space = response->allocated_len - response->len; - if (space < 128) { - int oldlen = response->len; - g_string_set_size(response, oldlen + 1024); - g_string_set_size(response, oldlen); - } - - /* Read another chunk of the response. */ - ret = sr_scpi_read_response(scpi, response, timeout); - if (ret < 0) - return ret; - if (ret > 0) - timeout = g_get_monotonic_time() + scpi->read_timeout_us; - } + g_mutex_lock(&scpi->scpi_mutex); + ret = scpi_get_data(scpi, command, scpi_response); + g_mutex_unlock(&scpi->scpi_mutex); - return SR_OK; + return ret; } /** @@ -796,13 +943,20 @@ SR_PRIV int sr_scpi_get_block(struct sr_scpi_dev_inst *scpi, long llen; long datalen; gint64 timeout; + va_list empty_va_list; + + g_mutex_lock(&scpi->scpi_mutex); if (command) - if (sr_scpi_send(scpi, command) != SR_OK) + if (scpi_send(scpi, command, empty_va_list) != SR_OK) { + g_mutex_unlock(&scpi->scpi_mutex); return SR_ERR; + } - if (sr_scpi_read_begin(scpi) != SR_OK) + if (sr_scpi_read_begin(scpi) != SR_OK) { + g_mutex_unlock(&scpi->scpi_mutex); return SR_ERR; + } /* * Assume an initial maximum length, optionally gets adjusted below. @@ -816,8 +970,9 @@ SR_PRIV int sr_scpi_get_block(struct sr_scpi_dev_inst *scpi, /* Get (the first chunk of) the response. */ while (response->len < 2) { - ret = sr_scpi_read_response(scpi, response, timeout); + ret = scpi_read_response(scpi, response, timeout); if (ret < 0) { + g_mutex_unlock(&scpi->scpi_mutex); g_string_free(response, TRUE); return ret; } @@ -835,6 +990,7 @@ SR_PRIV int sr_scpi_get_block(struct sr_scpi_dev_inst *scpi, * the input buffer, leaving just the data bytes. */ if (response->str[0] != '#') { + g_mutex_unlock(&scpi->scpi_mutex); g_string_free(response, TRUE); return SR_ERR_DATA; } @@ -842,13 +998,15 @@ SR_PRIV int sr_scpi_get_block(struct sr_scpi_dev_inst *scpi, buf[1] = '\0'; ret = sr_atol(buf, &llen); if ((ret != SR_OK) || (llen == 0)) { + g_mutex_unlock(&scpi->scpi_mutex); g_string_free(response, TRUE); return ret; } while (response->len < (unsigned long)(2 + llen)) { - ret = sr_scpi_read_response(scpi, response, timeout); + ret = scpi_read_response(scpi, response, timeout); if (ret < 0) { + g_mutex_unlock(&scpi->scpi_mutex); g_string_free(response, TRUE); return ret; } @@ -858,6 +1016,7 @@ SR_PRIV int sr_scpi_get_block(struct sr_scpi_dev_inst *scpi, buf[llen] = '\0'; ret = sr_atol(buf, &datalen); if ((ret != SR_OK) || (datalen == 0)) { + g_mutex_unlock(&scpi->scpi_mutex); g_string_free(response, TRUE); return ret; } @@ -875,8 +1034,9 @@ SR_PRIV int sr_scpi_get_block(struct sr_scpi_dev_inst *scpi, } while (response->len < (unsigned long)(datalen)) { - ret = sr_scpi_read_response(scpi, response, timeout); + ret = scpi_read_response(scpi, response, timeout); if (ret < 0) { + g_mutex_unlock(&scpi->scpi_mutex); g_string_free(response, TRUE); return ret; } @@ -884,6 +1044,8 @@ SR_PRIV int sr_scpi_get_block(struct sr_scpi_dev_inst *scpi, timeout = g_get_monotonic_time() + scpi->read_timeout_us; } + g_mutex_unlock(&scpi->scpi_mutex); + /* Convert received data to byte array. */ *scpi_response = g_byte_array_new_take( (guint8*)g_string_free(response, FALSE), datalen); @@ -971,3 +1133,125 @@ SR_PRIV void sr_scpi_hw_info_free(struct sr_scpi_hw_info *hw_info) g_free(hw_info->firmware_version); g_free(hw_info); } + +SR_PRIV const char *sr_vendor_alias(const char *raw_vendor) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(scpi_vendors); i++) { + if (!g_ascii_strcasecmp(raw_vendor, scpi_vendors[i][0])) + return scpi_vendors[i][1]; + } + + return raw_vendor; +} + +SR_PRIV const char *sr_scpi_cmd_get(const struct scpi_command *cmdtable, int command) +{ + unsigned int i; + const char *cmd; + + if (!cmdtable) + return NULL; + + cmd = NULL; + for (i = 0; cmdtable[i].string; i++) { + if (cmdtable[i].command == command) { + cmd = cmdtable[i].string; + break; + } + } + + return cmd; +} + +SR_PRIV int sr_scpi_cmd(const struct sr_dev_inst *sdi, const struct scpi_command *cmdtable, + int command, ...) +{ + struct sr_scpi_dev_inst *scpi; + va_list args; + int ret; + const char *cmd; + + if (!(cmd = sr_scpi_cmd_get(cmdtable, command))) { + /* Device does not implement this command, that's OK. */ + return SR_OK; + } + + scpi = sdi->conn; + va_start(args, command); + ret = sr_scpi_send_variadic(scpi, cmd, args); + va_end(args); + + return ret; +} + +SR_PRIV int sr_scpi_cmd_resp(const struct sr_dev_inst *sdi, + const struct scpi_command *cmdtable, + GVariant **gvar, const GVariantType *gvtype, int command, ...) +{ + struct sr_scpi_dev_inst *scpi; + va_list args; + const char *cmd; + GString *response; + char *s; + gboolean b; + double d; + int ret; + + scpi = sdi->conn; + + if (!(cmd = sr_scpi_cmd_get(cmdtable, command))) { + /* Device does not implement this command. */ + return SR_ERR_NA; + } + + g_mutex_lock(&scpi->scpi_mutex); + + va_start(args, command); + ret = scpi_send_variadic(scpi, cmd, args); + va_end(args); + if (ret != SR_OK) { + g_mutex_unlock(&scpi->scpi_mutex); + return ret; + } + + response = g_string_sized_new(1024); + ret = scpi_get_data(scpi, NULL, &response); + if (ret != SR_OK) { + g_mutex_unlock(&scpi->scpi_mutex); + if (response) + g_string_free(response, TRUE); + return ret; + } + + g_mutex_unlock(&scpi->scpi_mutex); + + /* Get rid of trailing linefeed if present */ + if (response->len >= 1 && response->str[response->len - 1] == '\n') + g_string_truncate(response, response->len - 1); + + /* Get rid of trailing carriage return if present */ + if (response->len >= 1 && response->str[response->len - 1] == '\r') + g_string_truncate(response, response->len - 1); + + s = g_string_free(response, FALSE); + + ret = SR_OK; + if (g_variant_type_equal(gvtype, G_VARIANT_TYPE_BOOLEAN)) { + if ((ret = parse_strict_bool(s, &b)) == SR_OK) + *gvar = g_variant_new_boolean(b); + } else if (g_variant_type_equal(gvtype, G_VARIANT_TYPE_DOUBLE)) { + if ((ret = sr_atod_ascii(s, &d)) == SR_OK) + *gvar = g_variant_new_double(d); + } else if (g_variant_type_equal(gvtype, G_VARIANT_TYPE_STRING)) { + *gvar = g_variant_new_string(s); + } else { + sr_err("Unable to convert to desired GVariant type."); + ret = SR_ERR_NA; + } + + g_free(s); + + return ret; +}