serial_forwarder: serial parsing

This removes the dependency on libserialport
This commit is contained in:
Zack Buhman 2024-06-22 23:45:08 -05:00
parent 8623d7cd86
commit 7a0518c35b
9 changed files with 286 additions and 117 deletions

View File

@ -5,10 +5,8 @@ DEBUG = -g -gdwarf-4
CFLAGS += -Wall -Werror -Wfatal-errors CFLAGS += -Wall -Werror -Wfatal-errors
CFLAGS += -std=c2x CFLAGS += -std=c2x
CFLAGS += -I$(SDL)/include -D_REENTRANT CFLAGS += -I$(SDL)/include -D_REENTRANT
CFLAGS += -I/usr/local/include
CFLAGS += $(shell pkg-config --cflags freetype2) CFLAGS += $(shell pkg-config --cflags freetype2)
LDFLAGS += -L$(SDL)/build -lSDL3 -Wl,-rpath=$(SDL)/build LDFLAGS += -L$(SDL)/build -lSDL3 -Wl,-rpath=$(SDL)/build
LDFLAGS += -Wl,-rpath,/usr/local/lib -L/usr/local/lib -lserialport
LDFLAGS += $(shell pkg-config --libs freetype2) LDFLAGS += $(shell pkg-config --libs freetype2)
DEPFLAGS = -MMD -MP DEPFLAGS = -MMD -MP
@ -18,12 +16,17 @@ OPT = -O3 -march=native
OBJS = \ OBJS = \
timer.o timer.o
all: main SERIAL_FORWARDER_OBJS = \
serial_forwarder.o \
serial.o \
parse_serial.o
all: serial_forwarder
%.o: %.c %.o: %.c
$(CC) $(CARCH) $(CFLAGS) $(OPT) $(DEBUG) $(DEPFLAGS) -MF ${<}.d -c $< -o $@ $(CC) $(CARCH) $(CFLAGS) $(OPT) $(DEBUG) $(DEPFLAGS) -MF ${<}.d -c $< -o $@
main: $(OBJS) serial_forwarder: $(SERIAL_FORWARDER_OBJS)
$(CC) $(LDFLAGS) $^ -o $@ $(CC) $(LDFLAGS) $^ -o $@
-include $(shell find -type f -name '*.d') -include $(shell find -type f -name '*.d')

4
event.h Normal file
View File

@ -0,0 +1,4 @@
enum command {
CMD_NOP,
CMD_STATE_TRANSFER,
};

View File

@ -1,9 +1,10 @@
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdbool.h> #include <stdbool.h>
#include <string.h>
#include "packet.h" #include "packet.h"
#include "bufsize.h" #include "parse_serial.h"
uint8_t const * parse_int(uint8_t const * buf, int * length, int * number, int * digits) uint8_t const * parse_int(uint8_t const * buf, int * length, int * number, int * digits)
{ {
@ -27,8 +28,8 @@ uint8_t const * parse_int(uint8_t const * buf, int * length, int * number, int *
case '8': [[fallthrough]]; case '8': [[fallthrough]];
case '9': case '9':
*digits += 1; *digits += 1;
number *= 10; *number *= 10;
number += c - '0'; *number += c - '0';
break; break;
case ' ': case ' ':
break; break;
@ -49,7 +50,7 @@ int parse_time(uint8_t const * const buf, int length, struct stopwatch_time * ti
uint8_t const * bufi = buf; uint8_t const * bufi = buf;
bufi = parse_int(bufi, &length, &time->integer_value, &time->integer_digits); bufi = parse_int(bufi, &length, &time->integer_value, &time->integer_digits);
if (integer_digits == 0) { if (time->integer_digits == 0) {
fprintf(stderr, "invalid integer at `%c`\n", *bufi); fprintf(stderr, "invalid integer at `%c`\n", *bufi);
return -1; return -1;
} }
@ -62,7 +63,7 @@ int parse_time(uint8_t const * const buf, int length, struct stopwatch_time * ti
return -1; return -1;
bufi = parse_int(bufi, &length, &time->fraction_value, &time->fraction_digits); bufi = parse_int(bufi, &length, &time->fraction_value, &time->fraction_digits);
if (integer_digits == 0) { if (time->fraction_digits == 0) {
fprintf(stderr, "invalid fraction at `%c`\n", *bufi); fprintf(stderr, "invalid fraction at `%c`\n", *bufi);
return -1; return -1;
} }
@ -74,55 +75,65 @@ int min(int a, int b) {
return a < b ? a : b; return a < b ? a : b;
} }
struct parser_state { bool is_timer_resume(uint8_t const * const buf, int length)
uint8_t buf[BUFSIZE];
int offset;
};
bool is_()
{ {
return false;
} }
void parse_line(uint8_t * buf, int length, bool handle_line(uint8_t const * const buf, int length,
struct timer_state * timer_state) struct timer_state * timer_state)
{ {
if (length == 0) { if (length == 0) {
timer_state->status = COUNTER_RUNNING; timer_state->status = TIMER_RUNNING;
int ret = clock_gettime(CLOCK_MONOTONIC, &timer_state->counter.start); int ret = clock_gettime(CLOCK_MONOTONIC, &timer_state->counter.start);
timer_state->counter.offset.tv_sec = 0; timer_state->counter.offset.tv_sec = 0;
timer_state->counter.offset.tv_nsec = 0; timer_state->counter.offset.tv_nsec = 0;
assert(ret != -1); assert(ret != -1);
} else if (is_) { return true;
} else if (is_timer_resume(buf, length)) {
return true;
} else {
int ret = parse_time(buf, length, &timer_state->time);
if (ret == 0) {
timer_state->status = TIMER_STOPPED;
return true;
}
} }
return false;
} }
void handle_parse(uint8_t * read_buf, int length, bool handle_parse(uint8_t * read_buf, int length,
struct parser_state * parser_state, struct parser_state * parser_state,
struct timer_state * timer_state) struct timer_state * timer_state)
{ {
bool have_state_change = false;
while (length > 0) { while (length > 0) {
int copy_length = min(length, BUFSIZE - state->offset); int copy_length = min(length, BUFSIZE - parser_state->offset);
length -= copy_length; length -= copy_length;
memcpy(&state->buf[state->offset], read_buf, copy_length); memcpy(&parser_state->buf[parser_state->offset], read_buf, copy_length);
state->offset += copy_length; parser_state->offset += copy_length;
while (state->offset > 0) { while (parser_state->offset > 0) {
int i = 0; int i = 0;
while (i < state->offset) { while (i < parser_state->offset) {
if (state->buf[i] == '\r') { if (parser_state->buf[i] == '\r') {
fprintf(stderr, "r at %d %d\n", i, state->offset); fprintf(stderr, "r at %d %d\n", i, parser_state->offset);
handle_line(state->buf, i); have_state_change |= handle_line(parser_state->buf, i,
state->offset -= i + 1; timer_state);
assert(state->offset >= 0); parser_state->offset -= i + 1;
memmove(&state->buf[0], &state->buf[i + 1], state->offset); assert(parser_state->offset >= 0);
memmove(&parser_state->buf[0], &parser_state->buf[i + 1], parser_state->offset);
break; break;
} }
i++; i++;
if (i == state->offset) if (i == parser_state->offset)
goto exit; goto exit;
} }
} }
exit: exit:
continue; continue;
} }
return have_state_change;
} }

15
parse_serial.h Normal file
View File

@ -0,0 +1,15 @@
#pragma once
#include <stdint.h>
#include "bufsize.h"
#include "timer.h"
struct parser_state {
uint8_t buf[BUFSIZE];
int offset;
};
bool handle_parse(uint8_t * read_buf, int length,
struct parser_state * parser_state,
struct timer_state * timer_state);

50
serial.c Normal file
View File

@ -0,0 +1,50 @@
#define _DEFAULT_SOURCE 1
#include <termios.h>
#include <stdio.h>
#include "serial.h"
int set_terminal_attributes(int fd)
{
int ret;
struct termios tty;
ret = tcgetattr(fd, &tty);
if (ret == -1) {
perror("tcgetattr");
return -1;
}
tty.c_cflag &= ~PARENB; // no parity
tty.c_cflag &= ~CSTOPB; // one stop bit
tty.c_cflag &= ~CSIZE; // clear size bits
tty.c_cflag |= CS8; // 8 bit
tty.c_cflag &= ~CRTSCTS; // disable RTS/CTS hardware flow control
tty.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines
tty.c_lflag &= ~ICANON;
tty.c_lflag &= ~ECHO; // disable echo
tty.c_lflag &= ~ECHOE; // disable erasure
tty.c_lflag &= ~ECHONL; // disable new-line echo
tty.c_lflag &= ~ISIG; // disable interpretation of INTR, QUIT and SUSP
tty.c_iflag &= ~(IXON|IXOFF|IXANY); // turn off xon/xoff ctrl
tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL); // disable any special handling of received bytes
tty.c_oflag &= ~OPOST; // prevent special interpretation of output bytes (e.g. newline chars)
tty.c_oflag &= ~ONLCR; // prevent conversion of newline to carriage return/line feed
tty.c_cc[VMIN] = 1;
tty.c_cc[VTIME] = 0;
cfsetospeed(&tty, B1200);
cfsetispeed(&tty, B1200);
ret = tcsetattr(fd, TCSANOW, &tty);
if (ret == -1) {
perror("tcsetattr");
return -1;
}
return 0;
}

3
serial.h Normal file
View File

@ -0,0 +1,3 @@
#pragma once
int set_terminal_attributes(int fd);

BIN
serial_forwarder Executable file

Binary file not shown.

View File

@ -1,79 +1,23 @@
#include <arpa/inet.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/epoll.h> #include <sys/epoll.h>
#include <errno.h> #include <sys/socket.h>
#include <stdbool.h> #include <sys/timerfd.h>
#include <unistd.h>
#include <libserialport.h>
#include "bufsize.h" #include "bufsize.h"
#include "serial.h"
#include "timer.h"
#include "parse_serial.h"
#define PORT 4321 #define PORT 4321
#define SERIALPORT "/dev/ttyS0" //#define SERIALPORT "/dev/ttyS0"
#define SERIALPORT "/dev/ttyUSB0"
int sp_error(const char * s, enum sp_return result)
{
char * error_message;
switch (result) {
case SP_ERR_ARG:
printf("%s: error: Invalid argument.\n", s);
case SP_ERR_FAIL:
error_message = sp_last_error_message();
printf("%s: error: Failed: %s\n", error_message, s);
sp_free_error_message(error_message);
case SP_ERR_SUPP:
printf("%s: error: Not supported.\n", s);
case SP_ERR_MEM:
printf("%s: error: Couldn't allocate memory.\n", s);
case SP_OK: [[fallthrough]];
default:
return result;
}
}
int init_serial(struct sp_port * port)
{
enum sp_return sp_ret;
sp_ret = sp_get_port_by_name(SERIALPORT, &port);
if (sp_ret != SP_OK) {
sp_error("sp_get_port_by_name", sp_ret);
return -1;
}
sp_ret = sp_open(port, SP_MODE_READ_WRITE);
if (sp_ret != SP_OK) {
sp_error("sp_open", sp_ret);
return -1;
}
sp_ret = sp_set_baudrate(port, 1200);
if (sp_ret != SP_OK) {
sp_error("sp_set_baudrate", sp_ret);
return -1;
}
sp_ret = sp_set_bits(port, 8);
if (sp_ret != SP_OK) {
sp_error("sp_set_bits", sp_ret);
return -1;
}
sp_ret = sp_set_parity(port, SP_PARITY_NONE);
if (sp_ret != SP_OK) {
sp_error("sp_set_parity", sp_ret);
return -1;
}
sp_ret = sp_set_stopbits(port, 1);
if (sp_ret != SP_OK) {
sp_error("sp_set_stopbits", sp_ret);
return -1;
}
sp_ret = sp_set_flowcontrol(port, SP_FLOWCONTROL_NONE);
if (sp_ret != SP_OK) {
sp_error("sp_set_flowcontrol", sp_ret);
return -1;
}
}
int handle_sock(int sock) int handle_sock(int sock)
{ {
@ -86,10 +30,10 @@ int handle_sock(int sock)
ssize_t recv_len = recvfrom(sock, buf, BUFSIZE, 0, (struct sockaddr *)&src_addr, &addrlen); ssize_t recv_len = recvfrom(sock, buf, BUFSIZE, 0, (struct sockaddr *)&src_addr, &addrlen);
if (recv_len == -1) { if (recv_len == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) { if (errno == EAGAIN || errno == EWOULDBLOCK) {
perror("eagain"); perror("sock eagain");
return 0; return 0;
} else { } else {
perror("recvfrom"); perror("recvfrom sock");
return -1; return -1;
} }
} }
@ -101,11 +45,85 @@ int handle_sock(int sock)
} }
} }
int rearm_timer(int timerfd)
{
struct itimerspec value;
value.it_value.tv_sec = 1;
value.it_value.tv_nsec = 0;
value.it_interval.tv_sec = 0;
value.it_interval.tv_nsec = 0;
int ret = timerfd_settime(timerfd, 0, &value, NULL);
if (ret == -1) {
perror("timerfd_settime");
return -1;
}
return 0;
}
int handle_timerfd(int timerfd)
{
uint64_t expired_count = 0;
while (true) {
ssize_t len = read(timerfd, &expired_count, (sizeof (expired_count)));
if (len == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
fprintf(stderr, "timerfd eagain\n");
break;
} else {
perror("read timerfd");
return -1;
}
}
assert(len == (sizeof (expired_count)));
printf("len %ld\n", len);
}
rearm_timer(timerfd);
printf("do timer stuff\n");
return 0;
}
struct receiver_state { struct receiver_state {
uint64_t seq; uint64_t seq;
uint64_t ack_seq; uint64_t ack_seq;
}; };
int handle_serialfd(int timerfd,
struct parser_state * parser_state,
struct timer_state * timer_state)
{
uint8_t buf[BUFSIZE];
while (true) {
ssize_t len = read(timerfd, buf, (sizeof (buf)));
if (len == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
fprintf(stderr, "serialfd eagain\n");
break;
} else {
perror("read serialfd");
return -1;
}
}
/*
for (int i = 0; i < len; i++) {
fprintf(stderr, "%x ", buf[i]);
}
fprintf(stderr, "\n");
*/
bool have_state_change = handle_parse(buf, len,
parser_state,
timer_state);
printf("have_state_change: %d\n", have_state_change);
}
return 0;
}
int main(void) int main(void)
{ {
int ret; int ret;
@ -127,7 +145,7 @@ int main(void)
return -1; return -1;
} }
#define MAX_EVENTS 2 #define MAX_EVENTS 5
struct epoll_event events[MAX_EVENTS]; struct epoll_event events[MAX_EVENTS];
int epollfd = epoll_create1(0); int epollfd = epoll_create1(0);
@ -136,19 +154,67 @@ int main(void)
return -1; return -1;
} }
struct epoll_event ev; {
ev.events = EPOLLIN | EPOLLET; struct epoll_event ev;
ev.data.fd = sock; ev.events = EPOLLIN | EPOLLET;
ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, sock, &ev); ev.data.fd = sock;
if (ret == -1) { ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, sock, &ev);
perror("epoll_ctl: sock"); if (ret == -1) {
perror("epoll_ctl: sock");
return -1;
}
}
int timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
if (timerfd == -1) {
perror("timerfd_create");
return -1; return -1;
} }
ret = rearm_timer(timerfd);
if (ret == -1) {
return -1;
}
if (0) {
struct epoll_event ev;
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = timerfd;
ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, timerfd, &ev);
if (ret == -1) {
perror("epoll_ctl: timerfd");
return -1;
}
}
int serialfd = open(SERIALPORT, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (serialfd == -1) {
perror("open: serialport");
return -1;
}
ret = set_terminal_attributes(serialfd);
if (ret == -1) {
return -1;
}
{
struct epoll_event ev;
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = serialfd;
ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, serialfd, &ev);
if (ret == -1) {
perror("epoll_ctl: timerfd");
return -1;
}
}
struct parser_state parser_state = {0};
struct timer_state timer_state = {0};
while (1) { while (1) {
printf("Wait for datagram\n"); printf("Wait for datagram\n");
int nfds = epoll_wait(epollfd, events, MAX_EVENTS, 1000); int nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
if (nfds == -1) { if (nfds == -1) {
perror("epoll_wait"); perror("epoll_wait");
return -1; return -1;
@ -159,6 +225,19 @@ int main(void)
ret = handle_sock(sock); ret = handle_sock(sock);
if (ret == -1) if (ret == -1)
return -1; return -1;
} else if (events[n].data.fd == timerfd) {
ret = handle_timerfd(timerfd);
if (ret == -1)
return -1;
} else if (events[n].data.fd == serialfd) {
fprintf(stderr, "handle_serialfd\n");
ret = handle_serialfd(serialfd,
&parser_state,
&timer_state);
if (ret == -1)
return -1;
} else {
assert(0);
} }
} }

16
timer.h
View File

@ -1,13 +1,17 @@
#pragma once
#include <time.h> #include <time.h>
enum counter_status { enum timer_status {
COUNTER_STOPPED, TIMER_STOPPED,
COUNTER_RUNNING, TIMER_RUNNING,
}; };
struct stopwatch_time { struct stopwatch_time {
int32_t whole; int integer_value;
int32_t fraction; int integer_digits;
int fraction_value;
int fraction_digits;
}; };
struct running_counter { struct running_counter {
@ -16,7 +20,7 @@ struct running_counter {
}; };
struct timer_state { struct timer_state {
enum counter_status status; enum timer_status status;
struct running_counter counter; struct running_counter counter;
struct stopwatch_time time; struct stopwatch_time time;
}; };