#include "beaglelogic.h"
static const uint32_t scanopts[] = {
+ SR_CONF_CONN,
SR_CONF_NUM_LOGIC_CHANNELS,
};
/* Default non-zero values (if any) */
devc->fd = -1;
devc->limit_samples = (uint64_t)-1;
+ devc->tcp_buffer = 0;
return devc;
}
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)
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,
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)
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;
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;
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;
}
/* 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;
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);
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2017 Kumar Abhishek <abhishek@theembeddedkitchen.net>
+ * Portions of the code are adopted from scpi_tcp.c and scpi.c
+ *
+ * Copyright (C) 2013 Martin Ling <martin-sigrok@earth.li>
+ * Copyright (C) 2013 poljar (Damir Jelić) <poljarinho@gmail.com>
+ *
+ * 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 <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 <errno.h>
+
+#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,
+};
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#ifdef _WIN32
+#define _WIN32_WINNT 0x0501
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <sys/socket.h>
+#endif
+#include <errno.h>
#include "protocol.h"
+#include "beaglelogic.h"
/* Define data packet size independent of packet (bufunitsize bytes) size
* from the BeagleLogic kernel module */
* 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;
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;
+}