Commit 9b2b3ef9 authored by Kumar Abhishek's avatar Kumar Abhishek Committed by Uwe Hermann

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: default avatarKumar Abhishek <abhishek@theembeddedkitchen.net>
parent 0bca2e75
......@@ -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 += \
......
......@@ -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);
......
......@@ -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
/*
* 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,
};
......@@ -21,7 +21,16 @@
#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 */
......@@ -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;
}
......@@ -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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment