1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-23 00:00:06 +01:00
libwebsockets/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-custom-client-transport/transport-serial.c

350 lines
7.1 KiB
C

/*
* lws-minimal-secure-streams-custom-client-transport
*
* Written in 2010-2021 by Andy Green <andy@warmcat.com>
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
*
*
* The serial port based custom transport
*/
#include "private.h"
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/ioctl.h>
#if defined(__linux__)
#include <asm/ioctls.h>
#include <linux/serial.h>
#endif
#include <errno.h>
/* debug helper */
void
lwsl_hexdump_level(int hexdump_level, const void *vbuf, size_t len)
{
unsigned char *buf = (unsigned char *)vbuf;
unsigned int n;
for (n = 0; n < len;) {
unsigned int start = n, m;
char line[80], *p = line;
p += snprintf(p, 10, "%04X: ", start);
for (m = 0; m < 16 && n < len; m++)
p += snprintf(p, 5, "%02X ", buf[n++]);
while (m++ < 16)
p += snprintf(p, 5, " ");
p += snprintf(p, 6, " ");
for (m = 0; m < 16 && (start + m) < len; m++) {
if (buf[start + m] >= ' ' && buf[start + m] < 127)
*p++ = (char)buf[start + m];
else
*p++ = '.';
}
while (m++ < 16)
*p++ = ' ';
*p++ = '\n';
*p = '\0';
_lws_log(hexdump_level, "%s", line);
(void)line;
}
_lws_log(hexdump_level, "\n");
}
/*
* Open and configure the serial transport fd
*/
int
open_serial_port(const char *filepath)
{
#if defined(__linux__)
struct serial_struct s_s;
#endif
struct termios tio;
int fd = open(filepath, O_RDWR);
if (fd == -1) {
lwsl_err("Unable to open %s\n", filepath);
return -1;
}
fcntl(fd, F_SETFL, O_NONBLOCK);
tcflush(fd, TCIOFLUSH);
#if defined(__linux__)
if (ioctl(fd, TIOCGSERIAL, &s_s) == 0) {
s_s.closing_wait = ASYNC_CLOSING_WAIT_NONE;
ioctl(fd, TIOCSSERIAL, &s_s);
}
#endif
/* enforce suitable tty state */
memset(&tio, 0, sizeof tio);
if (tcgetattr(fd, &tio)) {
close(fd);
fd = -1;
return -1;
}
cfsetispeed(&tio, B2000000);
cfsetospeed(&tio, B2000000);
tio.c_lflag &= (tcflag_t)~(ISIG | ICANON | IEXTEN | ECHO |
#if defined(__linux__)
XCASE |
#endif
ECHOE | ECHOK | ECHONL | ECHOCTL | ECHOKE);
tio.c_iflag &= (tcflag_t)~(INLCR | IGNBRK | IGNPAR | IGNCR | ICRNL |
IMAXBEL | IXON | IXOFF | IXANY
#if defined(__linux__)
| IUCLC
#endif
| 0xff);
tio.c_oflag = 0;
tio.c_cc[VMIN] = 1;
tio.c_cc[VTIME] = 0;
tio.c_cc[VEOF] = 1;
#if 0
tio.c_cflag = tio.c_cflag & (unsigned long) ~(
#if defined(__linux__)
CBAUD |
#endif
CSIZE | CSTOPB | PARENB | CRTSCTS);
#endif
tio.c_cflag = B2000000 | /*0x1412 | */ CS8 | CREAD | CLOCAL;
tcsetattr(fd, TCSANOW, &tio);
return fd;
}
int
open_transport_file(custom_poll_ctx_t *cpcx, const char *filepath, void *priv)
{
int fd = open_serial_port(filepath);
if (fd < 0)
return -1;
transport_fd = fd;
/* let's add it to the event loop, and set POLLIN */
if (custom_poll_add_fd(cpcx, fd, POLLIN, priv)) {
close(fd);
return -1;
}
return fd;
}
/****** custom transport to proxy
*
*
**/
/* incoming parsed channel cbs */
static int
ltm_ch_payload(lws_transport_mux_ch_t *tmc, const uint8_t *buf, size_t len)
{
lwsl_notice("%s\n", __func__);
return 0;
}
static int
ltm_ch_opens_serial(lws_transport_mux_ch_t *tmc, int determination)
{
lws_transport_mux_t *tm = lws_container_of(tmc->list.owner,
lws_transport_mux_t, owner);
struct lws_sspc_handle *h = (struct lws_sspc_handle *)tmc->priv;
assert_is_tm(tm);
lwsl_sspc_err(h, "%d", determination);
if (tm->info.txp_cpath.ops_in->event_connect_disposition(h, determination))
return -1;
return 0;
}
static int
ltm_ch_closes(lws_transport_mux_ch_t *tmc)
{
lwsl_notice("%s\n", __func__);
return 0;
}
static void
ltm_txp_req_write(lws_transport_mux_t *tm)
{
a_cpcx.tm->info.txp_cpath.ops_onw->req_write(
a_cpcx.tm->info.txp_cpath.priv_onw);
}
static int
ltm_txp_can_write(lws_transport_mux_ch_t *tmc)
{
assert_is_tmch(tmc);
return lws_txp_inside_sspc.event_can_write((struct lws_sspc_handle *)tmc->priv, 2048);
}
static const lws_txp_mux_parse_cbs_t cbs = {
.payload = ltm_ch_payload,
.ch_opens = ltm_ch_opens_serial,
.ch_closes = ltm_ch_closes,
.txp_req_write = ltm_txp_req_write,
.txp_can_write = ltm_txp_can_write,
};
int
custom_transport_event(struct pollfd *pfd, void *priv)
{
uint8_t buf[2048];
ssize_t r = sizeof(buf);
lwsl_notice("%s: fd %d, revents %d\n", __func__, pfd->fd, pfd->revents);
if (pfd->revents & POLLOUT) {
custom_poll_change_fd(&a_cpcx, pfd->fd, 0, POLLOUT);
/*
* We can write something on the transport... if the transport
* mux layer has something, let that use the write preferentally
* and request another write for whatever this was
*/
lwsl_notice("%s: doing POLLOUT\n", __func__);
if (lws_transport_mux_pending(a_cpcx.tm, buf, (size_t *)&r, &cbs)) {
lws_transport_path_client_dump(&a_cpcx.tm->info.txp_cpath, "cpath");
a_cpcx.tm->info.txp_cpath.ops_onw->_write(
a_cpcx.tm->info.txp_cpath.priv_onw,
buf, (size_t)r);
return 0;
}
}
if (pfd->revents & POLLIN) {
r = read(pfd->fd, buf, sizeof(buf));
if (r < 0) {
int eno = errno;
lwsl_warn("%s: read says %d, errno %d\n", __func__,
(int)r, eno);
return -1;
}
//lwsl_hexdump_notice(buf, (size_t)r);
if (a_cpcx.tm && a_cpcx.tm->info.txp_cpath.ops_in) {
#if 0
lwsl_user("%s: passing read to %s, priv_in %p\n",
__func__,
a_cpcx.tm->info.txp_cpath.ops_in->name,
a_cpcx.tm->info.txp_cpath.priv_in);
#endif
a_cpcx.tm->info.txp_cpath.ops_in->event_read(
a_cpcx.tm->info.txp_cpath.priv_in,
buf, (size_t)r);
}
}
return 0;
}
/*
* We get called while an individual SS is trying to connect to the proxy to
* be recognized as operational. It's the equivalent of trying to bring up the
* Unix Domain socket
*/
static int
txp_serial_retry_connect(lws_txp_path_client_t *path,
struct lws_sspc_handle *h)
{
lwsl_user("%s\n", __func__);
if (path->ops_onw->event_connect_disposition(h,
a_cpcx.tm->link_state != LWSTM_OPERATIONAL))
return -1;
return 0;
}
static void
txp_serial_req_write(lws_transport_priv_t priv)
{
lwsl_notice("%s\n", __func__);
custom_poll_change_fd(&a_cpcx, transport_fd, POLLOUT, 0);
}
static int
txp_serial_write(lws_transport_priv_t priv, uint8_t *buf, size_t len)
{
lwsl_notice("%s: writing %u\n", __func__, (unsigned int)len);
// lwsl_hexdump_notice(buf, len);
if (write(transport_fd, buf, len) != (ssize_t)len) {
lwsl_warn("%s: write %u failed\n", __func__, (unsigned int)len);
return 1;
}
return 0;
}
static void
txp_serial_close(lws_transport_priv_t priv)
{
#if 0
struct lws *wsi = (struct lws *)priv;
if (!wsi)
return;
lws_set_opaque_user_data(wsi, NULL);
lws_wsi_close(wsi, LWS_TO_KILL_ASYNC);
*priv = NULL;
#endif
}
static void
txp_serial_stream_up(lws_transport_priv_t priv)
{
// struct lws *wsi = (struct lws *)priv;
// lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
}
const lws_transport_client_ops_t lws_sss_ops_client_serial = {
.name = "txpserial",
.event_retry_connect = txp_serial_retry_connect,
.req_write = txp_serial_req_write,
._write = txp_serial_write,
._close = txp_serial_close,
.event_stream_up = txp_serial_stream_up,
};