libsigrok/src/hardware/beaglelogic/beaglelogic_tcp.c

431 lines
9.3 KiB
C

/*
* This file is part of the libsigrok project.
*
* Copyright (C) 2017 Kumar Abhishek <abhishek@theembeddedkitchen.net>
* Portions of the code are adapted from scpi_tcp.c and scpi.c, their
* copyright notices are listed below:
*
* 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));
g_free(buf);
return SR_ERR;
}
if (out < (int)strlen(buf)) {
sr_dbg("Only sent %d/%zu 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;
}
SR_PRIV int beaglelogic_tcp_drain(struct dev_context *devc)
{
char *buf = g_malloc(1024);
fd_set rset;
int ret, len = 0;
struct timeval tv;
FD_ZERO(&rset);
FD_SET(devc->socket, &rset);
/* 25ms timeout */
tv.tv_sec = 0;
tv.tv_usec = 25 * 1000;
do {
ret = select(devc->socket + 1, &rset, NULL, NULL, &tv);
if (ret > 0)
len += beaglelogic_tcp_read_data(devc, buf, 1024);
} while (ret > 0);
sr_spew("Drained %d bytes of data.", len);
g_free(buf);
return SR_OK;
}
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;
*tcp_resp = NULL;
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)
{
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 %" PRIu32, 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);
if (err)
return err;
devc->cur_samplerate = arg;
return SR_OK;
}
static int beaglelogic_set_samplerate(struct dev_context *devc)
{
int ret;
char *resp;
beaglelogic_tcp_send_cmd(devc, "samplerate %" PRIu32,
(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 %" PRIu32, 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 %" PRIu32, 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)
{
beaglelogic_tcp_drain(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 %" PRIu32, 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)
{
(void)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,
};