From: Kumar Abhishek Date: Thu, 21 Sep 2017 16:24:17 +0000 (+0530) Subject: beaglelogic: Implement TCP protocol X-Git-Url: http://sigrok.org/gitweb/?p=libsigrok.git;a=commitdiff_plain;h=9b2b3ef93e9821ffc9edc1c8bdd929c0143939dd beaglelogic: Implement TCP protocol BeagleLogic now supports two modes of interface - one being the native mode running on an ARM system like the BeagleBone Black and the other mode acting like a TCP client for the BeagleLogic server running off a BeagleBone compatible system. This makes it convenient for desktop users to retrieve samples from BeagleLogic, no more copying files and SSHing into the BeagleBone hardware in order to use BeagleLogic. Signed-off-by: Kumar Abhishek --- diff --git a/Makefile.am b/Makefile.am index b2ea8fe0..cedd8250 100644 --- a/Makefile.am +++ b/Makefile.am @@ -230,7 +230,8 @@ src_libdrivers_la_SOURCES += \ src/hardware/beaglelogic/protocol.h \ src/hardware/beaglelogic/protocol.c \ src/hardware/beaglelogic/api.c \ - src/hardware/beaglelogic/beaglelogic_native.c + src/hardware/beaglelogic/beaglelogic_native.c \ + src/hardware/beaglelogic/beaglelogic_tcp.c endif if HW_BRYMEN_BM86X src_libdrivers_la_SOURCES += \ diff --git a/src/hardware/beaglelogic/api.c b/src/hardware/beaglelogic/api.c index ef3e8ffa..2d21f46d 100644 --- a/src/hardware/beaglelogic/api.c +++ b/src/hardware/beaglelogic/api.c @@ -22,6 +22,7 @@ #include "beaglelogic.h" static const uint32_t scanopts[] = { + SR_CONF_CONN, SR_CONF_NUM_LOGIC_CHANNELS, }; @@ -67,6 +68,7 @@ static struct dev_context *beaglelogic_devc_alloc(void) /* Default non-zero values (if any) */ devc->fd = -1; devc->limit_samples = (uint64_t)-1; + devc->tcp_buffer = 0; return devc; } @@ -77,17 +79,35 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options) struct sr_config *src; struct sr_dev_inst *sdi; struct dev_context *devc; + const char *conn = NULL; + gchar **params; int i, maxch; - /* Probe for /dev/beaglelogic */ - if (!g_file_test(BEAGLELOGIC_DEV_NODE, G_FILE_TEST_EXISTS)) - return NULL; - maxch = NUM_CHANNELS; for (l = options; l; l = l->next) { src = l->data; if (src->key == SR_CONF_NUM_LOGIC_CHANNELS) maxch = g_variant_get_int32(src->data); + if (src->key == SR_CONF_CONN) + conn = g_variant_get_string(src->data, NULL); + } + + /* Probe for /dev/beaglelogic if not connecting via TCP */ + if (conn == NULL) { + if (!g_file_test(BEAGLELOGIC_DEV_NODE, G_FILE_TEST_EXISTS)) + return NULL; + } else { + params = g_strsplit(conn, "/", 0); + if (!params || !params[1] || !params[2]) { + sr_err("Invalid Parameters."); + g_strfreev(params); + return NULL; + } + if (g_ascii_strncasecmp(params[0], "tcp", 3)) { + sr_err("Only TCP (tcp-raw) protocol is currently supported."); + g_strfreev(params); + return NULL; + } } if (maxch > 8) @@ -102,8 +122,23 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options) devc = beaglelogic_devc_alloc(); - devc->beaglelogic = &beaglelogic_native_ops; - + if (conn == NULL) { + devc->beaglelogic = &beaglelogic_native_ops; + sr_info("BeagleLogic device found at "BEAGLELOGIC_DEV_NODE); + } else { + devc->read_timeout = 1000 * 1000; + devc->beaglelogic = &beaglelogic_tcp_ops; + devc->address = g_strdup(params[1]); + devc->port = g_strdup(params[2]); + g_strfreev(params); + + if (devc->beaglelogic->open(devc) != SR_OK) + goto err_free; + if (beaglelogic_tcp_detect(devc) != SR_OK) + goto err_free; + sr_info("BeagleLogic device found at %s : %s", + devc->address, devc->port); + } /* Fill the channels */ for (i = 0; i < maxch; i++) sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, TRUE, @@ -111,10 +146,14 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options) sdi->priv = devc; - /* Signal */ - sr_info("BeagleLogic device found at "BEAGLELOGIC_DEV_NODE); - return std_scan_complete(di, g_slist_append(NULL, sdi)); +err_free: + g_free(sdi->model); + g_free(sdi->version); + g_free(devc); + g_free(sdi); + + return NULL; } static int dev_open(struct sr_dev_inst *sdi) @@ -122,11 +161,15 @@ static int dev_open(struct sr_dev_inst *sdi) struct dev_context *devc = sdi->priv; /* Open BeagleLogic */ - if (devc->beaglelogic->open(devc)) - return SR_ERR; + if (devc->beaglelogic == &beaglelogic_native_ops) + if (devc->beaglelogic->open(devc)) + return SR_ERR; /* Set fd and local attributes */ - devc->pollfd.fd = devc->fd; + if (devc->beaglelogic == &beaglelogic_tcp_ops) + devc->pollfd.fd = devc->socket; + else + devc->pollfd.fd = devc->fd; devc->pollfd.events = G_IO_IN; devc->pollfd.revents = 0; @@ -142,10 +185,14 @@ static int dev_open(struct sr_dev_inst *sdi) devc->beaglelogic->set_triggerflags(devc); /* Map the kernel capture FIFO for reads, saves 1 level of memcpy */ - if (devc->beaglelogic->mmap(devc) != SR_OK) { - sr_err("Unable to map capture buffer"); - devc->beaglelogic->close(devc); - return SR_ERR; + if (devc->beaglelogic == &beaglelogic_native_ops) { + if (devc->beaglelogic->mmap(devc) != SR_OK) { + sr_err("Unable to map capture buffer"); + devc->beaglelogic->close(devc); + return SR_ERR; + } + } else { + devc->tcp_buffer = g_malloc(TCP_BUFFER_SIZE); } return SR_OK; @@ -156,9 +203,13 @@ static int dev_close(struct sr_dev_inst *sdi) struct dev_context *devc = sdi->priv; /* Close the memory mapping and the file */ - devc->beaglelogic->munmap(devc); + if (devc->beaglelogic == &beaglelogic_native_ops) + devc->beaglelogic->munmap(devc); devc->beaglelogic->close(devc); + if (devc->tcp_buffer) + g_free(devc->tcp_buffer); + return SR_OK; } @@ -288,8 +339,13 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi) /* Trigger and add poll on file */ devc->beaglelogic->start(devc); - sr_session_source_add_pollfd(sdi->session, &devc->pollfd, - BUFUNIT_TIMEOUT_MS(devc), beaglelogic_receive_data, + if (devc->beaglelogic == &beaglelogic_native_ops) + sr_session_source_add_pollfd(sdi->session, &devc->pollfd, + BUFUNIT_TIMEOUT_MS(devc), beaglelogic_native_receive_data, + (void *)sdi); + else + sr_session_source_add_pollfd(sdi->session, &devc->pollfd, + BUFUNIT_TIMEOUT_MS(devc), beaglelogic_tcp_receive_data, (void *)sdi); return SR_OK; @@ -303,7 +359,8 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi) devc->beaglelogic->stop(devc); /* lseek to offset 0, flushes the cache */ - lseek(devc->fd, 0, SEEK_SET); + if (devc->beaglelogic == &beaglelogic_native_ops) + lseek(devc->fd, 0, SEEK_SET); /* Remove session source and send EOT packet */ sr_session_source_remove_pollfd(sdi->session, &devc->pollfd); diff --git a/src/hardware/beaglelogic/beaglelogic.h b/src/hardware/beaglelogic/beaglelogic.h index 11bd1491..3ee0fd69 100644 --- a/src/hardware/beaglelogic/beaglelogic.h +++ b/src/hardware/beaglelogic/beaglelogic.h @@ -124,5 +124,8 @@ struct beaglelogic_ops { }; SR_PRIV extern const struct beaglelogic_ops beaglelogic_native_ops; +SR_PRIV extern const struct beaglelogic_ops beaglelogic_tcp_ops; + +SR_PRIV int beaglelogic_tcp_detect(struct dev_context *devc); #endif diff --git a/src/hardware/beaglelogic/beaglelogic_tcp.c b/src/hardware/beaglelogic/beaglelogic_tcp.c new file mode 100644 index 00000000..8baf4eef --- /dev/null +++ b/src/hardware/beaglelogic/beaglelogic_tcp.c @@ -0,0 +1,367 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2017 Kumar Abhishek + * Portions of the code are adopted from scpi_tcp.c and scpi.c + * + * Copyright (C) 2013 Martin Ling + * Copyright (C) 2013 poljar (Damir Jelić) + * + * 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 +#ifdef _WIN32 +#define _WIN32_WINNT 0x0501 +#include +#include +#endif +#include +#include +#include +#ifndef _WIN32 +#include +#include +#include +#include +#endif +#include + +#include "protocol.h" +#include "beaglelogic.h" + +static int beaglelogic_tcp_open(struct dev_context *devc) { + struct addrinfo hints; + struct addrinfo *results, *res; + int err; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + err = getaddrinfo(devc->address, devc->port, &hints, &results); + + if (err) { + sr_err("Address lookup failed: %s:%s: %s", devc->address, + devc->port, gai_strerror(err)); + return SR_ERR; + } + + for (res = results; res; res = res->ai_next) { + if ((devc->socket = socket(res->ai_family, res->ai_socktype, + res->ai_protocol)) < 0) + continue; + if (connect(devc->socket, res->ai_addr, res->ai_addrlen) != 0) { + close(devc->socket); + devc->socket = -1; + continue; + } + break; + } + + freeaddrinfo(results); + + if (devc->socket < 0) { + sr_err("Failed to connect to %s:%s: %s", devc->address, + devc->port, g_strerror(errno)); + return SR_ERR; + } + + return SR_OK; +} + +static int beaglelogic_tcp_send_cmd(struct dev_context *devc, + const char *format, ...) { + int len, out; + va_list args, args_copy; + char *buf; + + va_start(args, format); + va_copy(args_copy, args); + len = vsnprintf(NULL, 0, format, args_copy); + va_end(args_copy); + + buf = g_malloc0(len + 2); + vsprintf(buf, format, args); + va_end(args); + + if (buf[len - 1] != '\n') + buf[len] = '\n'; + + out = send(devc->socket, buf, strlen(buf), 0); + + if (out < 0) { + sr_err("Send error: %s", g_strerror(errno)); + return SR_ERR; + } + + if (out < (int)strlen(buf)) { + sr_dbg("Only sent %d/%d bytes of command: '%s'.", out, + strlen(buf), buf); + } + + sr_spew("Sent command: '%s'.", buf); + g_free(buf); + + return SR_OK; +} + +static int beaglelogic_tcp_read_data(struct dev_context *devc, char *buf, + int maxlen) { + int len; + + len = recv(devc->socket, buf, maxlen, 0); + + if (len < 0) { + sr_err("Receive error: %s", g_strerror(errno)); + return SR_ERR; + } + + return len; +} + +static int beaglelogic_tcp_get_string(struct dev_context *devc, const char *cmd, + char **tcp_resp) { + GString *response = g_string_sized_new(1024); + int len; + gint64 timeout; + + if (cmd) { + if (beaglelogic_tcp_send_cmd(devc, cmd) != SR_OK) { + return SR_ERR; + } + } + + timeout = g_get_monotonic_time() + devc->read_timeout; + len = beaglelogic_tcp_read_data(devc, response->str, + response->allocated_len); + + if (len < 0) { + g_string_free(response, TRUE); + return SR_ERR; + } + + if (len > 0) { + g_string_set_size(response, len); + } + + if (g_get_monotonic_time() > timeout) { + sr_err("Timed out waiting for response."); + g_string_free(response, TRUE); + return SR_ERR_TIMEOUT; + } + + /* Remove trailing newline if present */ + if (response->len >= 1 && response->str[response->len - 1] == '\n') + g_string_truncate(response, response->len - 1); + + /* Remove trailing carriage return if present */ + if (response->len >= 1 && response->str[response->len - 1] == '\r') + g_string_truncate(response, response->len - 1); + + sr_spew("Got response: '%.70s', length %" G_GSIZE_FORMAT ".", + response->str, response->len); + + *tcp_resp = g_string_free(response, FALSE); + return SR_OK; +} + +static int beaglelogic_tcp_get_int(struct dev_context *devc, + const char *cmd, int *response) { + int ret; + char *resp = NULL; + + ret = beaglelogic_tcp_get_string(devc, cmd, &resp); + if (!resp && ret != SR_OK) + return ret; + + if (sr_atoi(resp, response) == SR_OK) + ret = SR_OK; + else + ret = SR_ERR_DATA; + + g_free(resp); + + return ret; +} + +SR_PRIV int beaglelogic_tcp_detect(struct dev_context *devc) { + char *resp = NULL; + int ret; + + ret = beaglelogic_tcp_get_string(devc, "version", &resp); + if (ret == SR_OK && !g_ascii_strncasecmp(resp, "BeagleLogic", 11)) + ret = SR_OK; + else + ret = SR_ERR; + + g_free(resp); + return ret; +} + +static int beaglelogic_open(struct dev_context *devc) { + return beaglelogic_tcp_open(devc); +} + +static int beaglelogic_close(struct dev_context *devc) { + g_free(devc->address); + g_free(devc->port); + + if (close(devc->socket) < 0) + return SR_ERR; + + return SR_OK; +} + +static int beaglelogic_get_buffersize(struct dev_context *devc) { + return beaglelogic_tcp_get_int(devc, "memalloc", + (int *)&devc->buffersize); +} + +static int beaglelogic_set_buffersize(struct dev_context *devc) { + int ret; + char *resp; + + beaglelogic_tcp_send_cmd(devc, "memalloc %lu", devc->buffersize); + ret = beaglelogic_tcp_get_string(devc, NULL, &resp); + if (ret == SR_OK && !g_ascii_strncasecmp(resp, "ok", 2)) + ret = SR_OK; + else + ret = SR_ERR; + + g_free(resp); + return ret; +} + +static int beaglelogic_get_samplerate(struct dev_context *devc) { + int arg, err; + err = beaglelogic_tcp_get_int(devc, "samplerate", &arg); + devc->cur_samplerate = arg; + return err; +} + +static int beaglelogic_set_samplerate(struct dev_context *devc) { + int ret; + char *resp; + + beaglelogic_tcp_send_cmd(devc, "samplerate %lu", + (uint32_t)devc->cur_samplerate); + ret = beaglelogic_tcp_get_string(devc, NULL, &resp); + if (ret == SR_OK && !g_ascii_strncasecmp(resp, "ok", 2)) + ret = SR_OK; + else + ret = SR_ERR; + + g_free(resp); + return ret; +} + +static int beaglelogic_get_sampleunit(struct dev_context *devc) { + return beaglelogic_tcp_get_int(devc, "sampleunit", + (int *)&devc->sampleunit); +} + +static int beaglelogic_set_sampleunit(struct dev_context *devc) { + int ret; + char *resp; + + beaglelogic_tcp_send_cmd(devc, "sampleunit %lu", devc->sampleunit); + ret = beaglelogic_tcp_get_string(devc, NULL, &resp); + if (ret == SR_OK && !g_ascii_strncasecmp(resp, "ok", 2)) + ret = SR_OK; + else + ret = SR_ERR; + + g_free(resp); + return ret; +} + +static int beaglelogic_get_triggerflags(struct dev_context *devc) { + return beaglelogic_tcp_get_int(devc, "triggerflags", + (int *)&devc->triggerflags); +} + +static int beaglelogic_set_triggerflags(struct dev_context *devc) { + int ret; + char *resp; + + beaglelogic_tcp_send_cmd(devc, "triggerflags %lu", devc->triggerflags); + ret = beaglelogic_tcp_get_string(devc, NULL, &resp); + if (ret == SR_OK && !g_ascii_strncasecmp(resp, "ok", 2)) + ret = SR_OK; + else + ret = SR_ERR; + + g_free(resp); + return ret; +} + +static int beaglelogic_get_lasterror(struct dev_context *devc) { + devc->last_error = 0; + return SR_OK; +} + +static int beaglelogic_start(struct dev_context *devc) { + return beaglelogic_tcp_send_cmd(devc, "get"); +} + +static int beaglelogic_stop(struct dev_context *devc) { + return beaglelogic_tcp_send_cmd(devc, "close"); +} + +static int beaglelogic_get_bufunitsize(struct dev_context *devc) { + return beaglelogic_tcp_get_int(devc, "bufunitsize", + (int *)&devc->bufunitsize); +} + +static int beaglelogic_set_bufunitsize(struct dev_context *devc) { + int ret; + char *resp; + + beaglelogic_tcp_send_cmd(devc, "bufunitsize %ld", devc->bufunitsize); + ret = beaglelogic_tcp_get_string(devc, NULL, &resp); + if (ret == SR_OK && !g_ascii_strncasecmp(resp, "ok", 2)) + ret = SR_OK; + else + ret = SR_ERR; + + g_free(resp); + return ret; +} + +static int dummy(struct dev_context *devc) { + (devc); + return SR_ERR_NA; +} + +SR_PRIV const struct beaglelogic_ops beaglelogic_tcp_ops = { + .open = beaglelogic_open, + .close = beaglelogic_close, + .get_buffersize = beaglelogic_get_buffersize, + .set_buffersize = beaglelogic_set_buffersize, + .get_samplerate = beaglelogic_get_samplerate, + .set_samplerate = beaglelogic_set_samplerate, + .get_sampleunit = beaglelogic_get_sampleunit, + .set_sampleunit = beaglelogic_set_sampleunit, + .get_triggerflags = beaglelogic_get_triggerflags, + .set_triggerflags = beaglelogic_set_triggerflags, + .start = beaglelogic_start, + .stop = beaglelogic_stop, + .get_lasterror = beaglelogic_get_lasterror, + .get_bufunitsize = beaglelogic_get_bufunitsize, + .set_bufunitsize = beaglelogic_set_bufunitsize, + .mmap = dummy, + .munmap = dummy, +}; diff --git a/src/hardware/beaglelogic/protocol.c b/src/hardware/beaglelogic/protocol.c index 8f3ce265..2787b9ba 100644 --- a/src/hardware/beaglelogic/protocol.c +++ b/src/hardware/beaglelogic/protocol.c @@ -21,7 +21,16 @@ #include #include #include +#ifdef _WIN32 +#define _WIN32_WINNT 0x0501 +#include +#include +#else +#include +#endif +#include #include "protocol.h" +#include "beaglelogic.h" /* Define data packet size independent of packet (bufunitsize bytes) size * from the BeagleLogic kernel module */ @@ -32,7 +41,7 @@ * kernel buffers appropriately. It is up to the application which is * using libsigrok to decide how to deal with the data. */ -SR_PRIV int beaglelogic_receive_data(int fd, int revents, void *cb_data) +SR_PRIV int beaglelogic_native_receive_data(int fd, int revents, void *cb_data) { const struct sr_dev_inst *sdi; struct dev_context *devc; @@ -107,3 +116,86 @@ SR_PRIV int beaglelogic_receive_data(int fd, int revents, void *cb_data) return TRUE; } + +SR_PRIV int beaglelogic_tcp_receive_data(int fd, int revents, void *cb_data) +{ + const struct sr_dev_inst *sdi; + struct dev_context *devc; + struct sr_datafeed_packet packet; + struct sr_datafeed_logic logic; + + int len; + int pre_trigger_samples; + int trigger_offset; + uint32_t packetsize; + uint64_t bytes_remaining; + + if (!(sdi = cb_data) || !(devc = sdi->priv)) + return TRUE; + + packetsize = TCP_BUFFER_SIZE; + logic.unitsize = SAMPLEUNIT_TO_BYTES(devc->sampleunit); + + if (revents == G_IO_IN) { + sr_info("In callback G_IO_IN"); + + len = recv(fd, devc->tcp_buffer, TCP_BUFFER_SIZE, 0); + if (len < 0) { + sr_err("Receive error: %s", g_strerror(errno)); + return SR_ERR; + } + + packetsize = len; + + bytes_remaining = (devc->limit_samples * logic.unitsize) - + devc->bytes_read; + + /* Configure data packet */ + packet.type = SR_DF_LOGIC; + packet.payload = &logic; + logic.data = devc->tcp_buffer; + logic.length = MIN(packetsize, bytes_remaining); + + if (devc->trigger_fired) { + /* Send the incoming transfer to the session bus. */ + sr_session_send(sdi, &packet); + } else { + /* Check for trigger */ + trigger_offset = soft_trigger_logic_check(devc->stl, + logic.data, packetsize, &pre_trigger_samples); + if (trigger_offset > -1) { + devc->bytes_read += pre_trigger_samples * logic.unitsize; + trigger_offset *= logic.unitsize; + logic.length = MIN(packetsize - trigger_offset, + bytes_remaining); + logic.data += trigger_offset; + + sr_session_send(sdi, &packet); + + devc->trigger_fired = TRUE; + } + } + + /* Update byte count and offset (roll over if needed) */ + devc->bytes_read += logic.length; + if ((devc->offset += packetsize) >= devc->buffersize) { + /* One shot capture, we abort and settle with less than + * the required number of samples */ + if (devc->triggerflags) + devc->offset = 0; + else + packetsize = 0; + } + } + + /* EOF Received or we have reached the limit */ + if (devc->bytes_read >= devc->limit_samples * logic.unitsize || + packetsize == 0) { + /* Send EOA Packet, stop polling */ + std_session_send_df_end(sdi); + devc->beaglelogic->stop(devc); + sr_session_source_remove_pollfd(sdi->session, &devc->pollfd); + } + + return TRUE; +} diff --git a/src/hardware/beaglelogic/protocol.h b/src/hardware/beaglelogic/protocol.h index 3539a7a4..521673ee 100644 --- a/src/hardware/beaglelogic/protocol.h +++ b/src/hardware/beaglelogic/protocol.h @@ -32,6 +32,9 @@ #define SAMPLEUNIT_TO_BYTES(x) ((x) == 1 ? 1 : 2) +#define TCP_BUFFER_SIZE (128 * 1024) + +/** Private, per-device-instance driver context. */ struct dev_context { int max_channels; uint32_t fw_ver; @@ -39,6 +42,13 @@ struct dev_context { /* Operations */ const struct beaglelogic_ops *beaglelogic; + /* TCP Settings */ + char *address; + char *port; + int socket; + unsigned int read_timeout; + unsigned char *tcp_buffer; + /* Acquisition settings: see beaglelogic.h */ uint64_t cur_samplerate; uint64_t limit_samples; @@ -64,6 +74,7 @@ struct dev_context { gboolean trigger_fired; }; -SR_PRIV int beaglelogic_receive_data(int fd, int revents, void *cb_data); +SR_PRIV int beaglelogic_native_receive_data(int fd, int revents, void *cb_data); +SR_PRIV int beaglelogic_tcp_receive_data(int fd, int revents, void *cb_data); #endif