]> sigrok.org Git - libsigrok.git/commitdiff
scpi_tcp: use common TCP support in SCPI transport (tcp-raw, tcp-rigol)
authorGerhard Sittig <redacted>
Sun, 24 Sep 2023 08:42:00 +0000 (10:42 +0200)
committerGerhard Sittig <redacted>
Mon, 25 Sep 2023 17:30:18 +0000 (19:30 +0200)
Use the recently added sr_tcp_dev_inst approach to TCP communication in
the tcp-raw and tcp-rigol SCPI transports. Eliminates redundancy in the
implementation.

Address style nits in the process: Use size types for length specs. Be
explicit about bytes and character conversions: Bytes are sent across
the TCP socket, while SCPI exchange typically involves text messages.
The previously used send(2) and recv(2) calls just passed void pointers.

Comment on the specific semantics of Rigol length specs that preceed the
response data. And how raw TCP attempts to determine the completion of a
reception while this length spec is absent. The previous implementation
would not reveal the motivation of this non-trivial logic.

src/scpi/scpi_tcp.c

index 29e7a4394aa8a54c1c69871f5362151ea6ff66db..c85cf3eb93b0387ba3ecff420da9eb7af5052edc 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <config.h>
-#ifdef _WIN32
-#define _WIN32_WINNT 0x0501
-#include <winsock2.h>
-#include <ws2tcpip.h>
-#endif
-#include <glib.h>
-#include <string.h>
-#include <unistd.h>
-#ifndef _WIN32
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-#endif
+#include "config.h"
+
 #include <errno.h>
+#include <glib.h>
 #include <libsigrok/libsigrok.h>
+#include <string.h>
+
 #include "libsigrok-internal.h"
 #include "scpi.h"
 
 #define LOG_PREFIX "scpi_tcp"
 
-#define LENGTH_BYTES 4
+#define LENGTH_BYTES sizeof(uint32_t)
 
 struct scpi_tcp {
-       char *address;
-       char *port;
-       int socket;
-       char length_buf[LENGTH_BYTES];
-       int length_bytes_read;
-       int response_length;
-       int response_bytes_read;
+       struct sr_tcp_dev_inst *tcp_dev;
+       uint8_t length_buf[LENGTH_BYTES];
+       size_t length_bytes_read;
+       size_t response_length;
+       size_t response_bytes_read;
 };
 
 static int scpi_tcp_dev_inst_new(void *priv, struct drv_context *drvc,
@@ -65,9 +53,9 @@ static int scpi_tcp_dev_inst_new(void *priv, struct drv_context *drvc,
                return SR_ERR;
        }
 
-       tcp->address = g_strdup(params[1]);
-       tcp->port = g_strdup(params[2]);
-       tcp->socket = -1;
+       tcp->tcp_dev = sr_tcp_dev_inst_new(params[1], params[2]);
+       if (!tcp->tcp_dev)
+               return SR_ERR;
 
        return SR_OK;
 }
@@ -75,42 +63,11 @@ static int scpi_tcp_dev_inst_new(void *priv, struct drv_context *drvc,
 static int scpi_tcp_open(struct sr_scpi_dev_inst *scpi)
 {
        struct scpi_tcp *tcp = scpi->priv;
-       struct addrinfo hints;
-       struct addrinfo *results, *res;
-       int err;
+       int ret;
 
-       memset(&hints, 0, sizeof(hints));
-       hints.ai_family = AF_UNSPEC;
-       hints.ai_socktype = SOCK_STREAM;
-       hints.ai_protocol = IPPROTO_TCP;
-
-       err = getaddrinfo(tcp->address, tcp->port, &hints, &results);
-
-       if (err) {
-               sr_err("Address lookup failed: %s:%s: %s", tcp->address, tcp->port,
-                       gai_strerror(err));
-               return SR_ERR;
-       }
-
-       for (res = results; res; res = res->ai_next) {
-               if ((tcp->socket = socket(res->ai_family, res->ai_socktype,
-                                               res->ai_protocol)) < 0)
-                       continue;
-               if (connect(tcp->socket, res->ai_addr, res->ai_addrlen) != 0) {
-                       close(tcp->socket);
-                       tcp->socket = -1;
-                       continue;
-               }
-               break;
-       }
-
-       freeaddrinfo(results);
-
-       if (tcp->socket < 0) {
-               sr_err("Failed to connect to %s:%s: %s", tcp->address, tcp->port,
-                               g_strerror(errno));
-               return SR_ERR;
-       }
+       ret = sr_tcp_connect(tcp->tcp_dev);
+       if (ret != SR_OK)
+               return ret;
 
        return SR_OK;
 }
@@ -119,10 +76,15 @@ static int scpi_tcp_connection_id(struct sr_scpi_dev_inst *scpi,
                char **connection_id)
 {
        struct scpi_tcp *tcp = scpi->priv;
+       char conn_text[128];
+       int ret;
 
-       *connection_id = g_strdup_printf("%s/%s:%s",
-               scpi->prefix, tcp->address, tcp->port);
+       ret = sr_tcp_get_port_path(tcp->tcp_dev, scpi->prefix, '/',
+               conn_text, sizeof(conn_text));
+       if (ret != SR_OK)
+               return ret;
 
+       *connection_id = g_strdup(conn_text);
        return SR_OK;
 }
 
@@ -131,33 +93,36 @@ static int scpi_tcp_source_add(struct sr_session *session, void *priv,
 {
        struct scpi_tcp *tcp = priv;
 
-       return sr_session_source_add(session, tcp->socket, events, timeout,
-                       cb, cb_data);
+       return sr_tcp_source_add(session, tcp->tcp_dev,
+               events, timeout, cb, cb_data);
 }
 
 static int scpi_tcp_source_remove(struct sr_session *session, void *priv)
 {
        struct scpi_tcp *tcp = priv;
 
-       return sr_session_source_remove(session, tcp->socket);
+       return sr_tcp_source_remove(session, tcp->tcp_dev);
 }
 
+/* Transmit text, usually a command. tcp-raw and tcp-rigol modes. */
 static int scpi_tcp_send(void *priv, const char *command)
 {
        struct scpi_tcp *tcp = priv;
-       int len, out;
-
-       len = strlen(command);
-       out = send(tcp->socket, command, len, 0);
-
-       if (out < 0) {
+       const uint8_t *wrptr;
+       size_t wrlen, written;
+       int ret;
+
+       wrptr = (const uint8_t *)command;
+       wrlen = strlen(command);
+       ret = sr_tcp_write_bytes(tcp->tcp_dev, wrptr, wrlen);
+       if (ret < 0) {
                sr_err("Send error: %s", g_strerror(errno));
                return SR_ERR;
        }
-
-       if (out < len) {
-               sr_dbg("Only sent %d/%d bytes of SCPI command: '%s'.", out,
-                      len, command);
+       written = (size_t)ret;
+       if (written < wrlen) {
+               sr_dbg("Only sent %zu/%zu bytes of SCPI command: '%s'.",
+                       written, wrlen, command);
        }
 
        sr_spew("Successfully sent SCPI command: '%s'.", command);
@@ -165,6 +130,7 @@ static int scpi_tcp_send(void *priv, const char *command)
        return SR_OK;
 }
 
+/* Start reception across multiple read calls. tcp-raw and tcp-rigol modes. */
 static int scpi_tcp_read_begin(void *priv)
 {
        struct scpi_tcp *tcp = priv;
@@ -175,90 +141,126 @@ static int scpi_tcp_read_begin(void *priv)
        return SR_OK;
 }
 
+/* Receive response data. tcp-raw mode. */
 static int scpi_tcp_raw_read_data(void *priv, char *buf, int maxlen)
 {
        struct scpi_tcp *tcp = priv;
-       int len;
-
-       len = recv(tcp->socket, buf, maxlen, 0);
-
-       if (len < 0) {
+       uint8_t *rdptr;
+       size_t rdlen, rcvd;
+       int ret;
+
+       /* Get another chunk of receive data. */
+       rdptr = (uint8_t *)buf;
+       rdlen = maxlen;
+       ret = sr_tcp_read_bytes(tcp->tcp_dev, rdptr, rdlen, FALSE);
+       if (ret < 0) {
                sr_err("Receive error: %s", g_strerror(errno));
                return SR_ERR;
        }
-
+       rcvd = (size_t)ret;
+
+       /*
+        * Raw data mode (in contrast to Rigol mode). Prepare to answer
+        * the "completed" condition while the payload's length is not
+        * known. Pretend that the length buffer had been received.
+        * Assume that short reads correspond to the end of a response,
+        * while full reads of the caller specified size suggest that
+        * more data can follow.
+        */
        tcp->length_bytes_read = LENGTH_BYTES;
-       tcp->response_length = len < maxlen ? len : maxlen + 1;
-       tcp->response_bytes_read = len;
+       tcp->response_length = rcvd < rdlen ? rcvd : rdlen + 1;
+       tcp->response_bytes_read = rcvd;
 
-       return len;
+       return rcvd;
 }
 
+/* Transmit data of given length. tcp-raw mode. */
 static int scpi_tcp_raw_write_data(void *priv, char *buf, int len)
 {
        struct scpi_tcp *tcp = priv;
-       int sentlen;
-
-       sentlen = send(tcp->socket, buf, len, 0);
-
-       if (sentlen < 0) {
+       const uint8_t *wrptr;
+       size_t wrlen, sent;
+       int ret;
+
+       wrptr = (const uint8_t *)buf;
+       wrlen = len;
+       ret = sr_tcp_write_bytes(tcp->tcp_dev, wrptr, wrlen);
+       if (ret < 0) {
                sr_err("Send error: %s.", g_strerror(errno));
                return SR_ERR;
        }
+       sent = (size_t)ret;
 
-       return sentlen;
+       return sent;
 }
 
+/* Receive response data. tcp-rigol mode. */
 static int scpi_tcp_rigol_read_data(void *priv, char *buf, int maxlen)
 {
        struct scpi_tcp *tcp = priv;
-       int len;
-
-       if (tcp->length_bytes_read < LENGTH_BYTES) {
-               len = recv(tcp->socket, tcp->length_buf + tcp->length_bytes_read,
-                               LENGTH_BYTES - tcp->length_bytes_read, 0);
-               if (len < 0) {
+       uint8_t *rdptr;
+       size_t rdlen, rcvd;
+       int ret;
+
+       /*
+        * Rigol mode, chunks are prefixed by a length spec.
+        * Get more length bytes when we haven't read them before.
+        * Return "zero length read" if length has yet to get received.
+        * Otherwise get chunk length from length bytes buffer.
+        */
+       if (tcp->length_bytes_read < sizeof(tcp->length_buf)) {
+               rdptr = &tcp->length_buf[tcp->length_bytes_read];
+               rdlen = sizeof(tcp->length_buf) - tcp->length_bytes_read;
+               ret = sr_tcp_read_bytes(tcp->tcp_dev, rdptr, rdlen, FALSE);
+               if (ret < 0) {
                        sr_err("Receive error: %s", g_strerror(errno));
                        return SR_ERR;
                }
-
-               tcp->length_bytes_read += len;
-
-               if (tcp->length_bytes_read < LENGTH_BYTES)
+               rcvd = (size_t)ret;
+               tcp->length_bytes_read += rcvd;
+               if (tcp->length_bytes_read < sizeof(tcp->length_buf))
                        return 0;
-               else
-                       tcp->response_length = RL32(tcp->length_buf);
+               tcp->response_length = read_u32le(tcp->length_buf);
        }
 
+       /* Received more chunk data than announced size? Fatal. */
        if (tcp->response_bytes_read >= tcp->response_length)
                return SR_ERR;
 
-       len = recv(tcp->socket, buf, maxlen, 0);
-
-       if (len < 0) {
+       /* Read another chunk of the receive data. */
+       rdptr = (uint8_t *)buf;
+       rdlen = maxlen;
+       ret = sr_tcp_read_bytes(tcp->tcp_dev, rdptr, rdlen, FALSE);
+       if (ret < 0) {
                sr_err("Receive error: %s", g_strerror(errno));
                return SR_ERR;
        }
+       rcvd = (size_t)ret;
+       tcp->response_bytes_read += rcvd;
 
-       tcp->response_bytes_read += len;
-
-       return len;
+       return rcvd;
 }
 
+/* Check reception completion. tcp-raw and tcp-rigol modes. */
 static int scpi_tcp_read_complete(void *priv)
 {
        struct scpi_tcp *tcp = priv;
+       gboolean have_length, have_response;
 
-       return (tcp->length_bytes_read == LENGTH_BYTES &&
-                       tcp->response_bytes_read >= tcp->response_length);
+       have_length = tcp->length_bytes_read == LENGTH_BYTES;
+       have_response = tcp->response_bytes_read >= tcp->response_length;
+
+       return have_length && have_response;
 }
 
 static int scpi_tcp_close(struct sr_scpi_dev_inst *scpi)
 {
        struct scpi_tcp *tcp = scpi->priv;
+       int ret;
 
-       if (close(tcp->socket) < 0)
-               return SR_ERR;
+       ret = sr_tcp_disconnect(tcp->tcp_dev);
+       if (ret != SR_OK)
+               return ret;
 
        return SR_OK;
 }
@@ -267,8 +269,7 @@ static void scpi_tcp_free(void *priv)
 {
        struct scpi_tcp *tcp = priv;
 
-       g_free(tcp->address);
-       g_free(tcp->port);
+       sr_tcp_dev_inst_free(tcp->tcp_dev);
 }
 
 SR_PRIV const struct sr_scpi_dev_inst scpi_tcp_raw_dev = {