| /*
* sai-device tty monitoring
*
* Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*
* Logging / parsing of device ttys
*/
#include <libwebsockets.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <termios.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
#include "e-private.h"
static const char *pass = "Completed: PASS";
struct pss {
struct lws_context *context;
lws_sorted_usec_list_t sul;
char collation[128];
lws_usec_t earliest;
int pos;
int i;
int pass_match;
char draining;
char match;
};
struct lws *
sai_serial_try_open(struct lws_vhost *vh, const char *devpath, int _rate, int i)
{
lws_sock_file_fd_type u;
struct termios tio;
struct lws *wsi;
int fd, rate;
switch (_rate) {
case 115200:
rate = B115200;
break;
default:
lwsl_err("%s: unknown rate %d\n", __func__, _rate);
return NULL;
}
/* enforce suitable tty state */
memset(&tio, 0, sizeof tio);
tio.c_cflag = CS8 | CREAD | CLOCAL;
// tio.c_cc[VMIN] = 1;
// tio.c_cc[VTIME] = 5;
fd = lws_open(devpath, O_RDWR);
if (fd == -1) {
/*
* Even when it's locked, we ought to be able to open it
*/
lwsl_err("%s: Unable to open %s\n", __func__, devpath);
return NULL;
}
cfsetospeed(&tio, (speed_t)rate);
cfsetispeed(&tio, (speed_t)rate);
tcsetattr(fd, TCSANOW, &tio);
tcflush(fd, TCIOFLUSH);
u.filefd = (lws_filefd_type)(long long)fd;
wsi = lws_adopt_descriptor_vhost(vh, LWS_ADOPT_RAW_FILE_DESC, u,
"sai-serial", NULL);
if (!wsi) {
lwsl_err("%s: Failed to adopt fifo descriptor\n", __func__);
close(fd);
return NULL;
}
lws_set_opaque_user_data(wsi, (void *)(intptr_t)i);
return wsi;
}
static void
finished_cb(void *opaque)
{
interrupted = 1;
bad = 0;
}
static void
spill(struct pss *pss)
{
saicom_lp_add(ssh[pss->i + 1], pss->collation, (unsigned int)pss->pos);
pss->pos = 0;
pss->earliest = 0;
lws_sul_cancel(&pss->sul);
/*
* We have seen the pass string
*/
if (pss->match) {
saicom_lp_add(ssh[0], "sai-expect: completed\n", 22);
pss->match = 0;
pss->draining = 1;
saicom_lp_callback_on_drain(finished_cb, NULL);
}
}
static void
sul_cb(lws_sorted_usec_list_t *sul)
{
struct pss *pss = lws_container_of(sul, struct pss, sul);
spill(pss);
}
static int
callback_serial(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len)
{
struct pss *pss = (struct pss *)user;
lws_usec_t now = lws_now_usecs();
uint8_t buf[128];
int n, m;
switch (reason) {
case LWS_CALLBACK_RAW_ADOPT_FILE:
pss->i = (int)(intptr_t)lws_get_opaque_user_data(wsi);
pss->context = lws_get_context(wsi);
break;
case LWS_CALLBACK_RAW_RX_FILE:
/*
* Typically we can read this much faster than it gets new data,
* 115200 is only 12KB/sec. If we don't do something about
* collation locally, we will end up generating and sending a
* huge number of discrete log fragments just a few chars long
* each.
*
* Let's collate them up to 128 bytes, with a 150ms limit to
* holding pieces
*/
n = (int)read((int)(intptr_t)lws_get_socket_fd(wsi), buf, sizeof(buf));
if (n < 1) {
lwsl_debug("%s: read on stdwsi failed\n", __func__);
return -1;
}
if (pss->draining)
break;
for (m = 0; m < n; m++) {
pss->collation[pss->pos++] = (char)buf[m];
if (buf[m] == pass[pss->pass_match]) {
pss->pass_match++;
if (pss->pass_match == (int)strlen(pass)) {
/*
* We have seen the EOT match string,
* start that process when we spill
* this line so we can log it
*/
pss->match = 1;
pss->pass_match = 0;
}
} else
pss->pass_match = 0;
/*
* Let's spill on line boundaries too, if the line
* was long enough to take 2ms (24 x 115Kbps octets) to
* collect
*/
if ((buf[m] == '\n' && pss->pos > 1 && pss->earliest &&
(now - pss->earliest) > 2 * LWS_US_PER_MS) ||
pss->pos == sizeof(pss->collation))
/*
* spill due to fill collation buffer
*/
spill(pss);
}
if (pss->pos && !pss->earliest)
pss->earliest = now;
if (now - pss->earliest > 150 * LWS_US_PER_MS)
/*
* Spill because the earliest is stale
*/
spill(pss);
else
/*
* if we are holding on to something, and nothing else
* happens, spill 150ms after earliest
*/
if (pss->pos)
lws_sul_schedule(pss->context, 0, &pss->sul,
sul_cb, (150 * LWS_US_PER_MS) -
(now - pss->earliest));
break;
case LWS_CALLBACK_RAW_CLOSE_FILE:
break;
case LWS_CALLBACK_RAW_WRITEABLE_FILE:
// lwsl_notice("LWS_CALLBACK_RAW_WRITEABLE_FILE\n");
// if (lws_write(wsi, (uint8_t *)"hello-this-is-written-every-couple-of-seconds\r\n", 47, LWS_WRITE_RAW) != 47)
// return -1;
break;
default:
break;
}
return 0;
}
struct lws_protocols protocol_serial = {
.name = "sai-serial",
.callback = callback_serial,
.per_session_data_size = sizeof(struct pss),
.rx_buffer_size = 0,
};
|