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 += -std=c2x
CFLAGS += -I$(SDL)/include -D_REENTRANT
CFLAGS += -I/usr/local/include
CFLAGS += $(shell pkg-config --cflags freetype2)
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)
DEPFLAGS = -MMD -MP
@ -18,12 +16,17 @@ OPT = -O3 -march=native
OBJS = \
timer.o
all: main
SERIAL_FORWARDER_OBJS = \
serial_forwarder.o \
serial.o \
parse_serial.o
all: serial_forwarder
%.o: %.c
$(CC) $(CARCH) $(CFLAGS) $(OPT) $(DEBUG) $(DEPFLAGS) -MF ${<}.d -c $< -o $@
main: $(OBJS)
serial_forwarder: $(SERIAL_FORWARDER_OBJS)
$(CC) $(LDFLAGS) $^ -o $@
-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 <stdio.h>
#include <stdbool.h>
#include <string.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)
{
@ -27,8 +28,8 @@ uint8_t const * parse_int(uint8_t const * buf, int * length, int * number, int *
case '8': [[fallthrough]];
case '9':
*digits += 1;
number *= 10;
number += c - '0';
*number *= 10;
*number += c - '0';
break;
case ' ':
break;
@ -49,7 +50,7 @@ int parse_time(uint8_t const * const buf, int length, struct stopwatch_time * ti
uint8_t const * bufi = buf;
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);
return -1;
}
@ -62,7 +63,7 @@ int parse_time(uint8_t const * const buf, int length, struct stopwatch_time * ti
return -1;
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);
return -1;
}
@ -74,55 +75,65 @@ int min(int a, int b) {
return a < b ? a : b;
}
struct parser_state {
uint8_t buf[BUFSIZE];
int offset;
};
bool is_()
bool is_timer_resume(uint8_t const * const buf, int length)
{
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)
{
if (length == 0) {
timer_state->status = COUNTER_RUNNING;
timer_state->status = TIMER_RUNNING;
int ret = clock_gettime(CLOCK_MONOTONIC, &timer_state->counter.start);
timer_state->counter.offset.tv_sec = 0;
timer_state->counter.offset.tv_nsec = 0;
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;
}
}
void handle_parse(uint8_t * read_buf, int length,
return false;
}
bool handle_parse(uint8_t * read_buf, int length,
struct parser_state * parser_state,
struct timer_state * timer_state)
{
bool have_state_change = false;
while (length > 0) {
int copy_length = min(length, BUFSIZE - state->offset);
int copy_length = min(length, BUFSIZE - parser_state->offset);
length -= copy_length;
memcpy(&state->buf[state->offset], read_buf, copy_length);
state->offset += copy_length;
memcpy(&parser_state->buf[parser_state->offset], read_buf, copy_length);
parser_state->offset += copy_length;
while (state->offset > 0) {
while (parser_state->offset > 0) {
int i = 0;
while (i < state->offset) {
if (state->buf[i] == '\r') {
fprintf(stderr, "r at %d %d\n", i, state->offset);
handle_line(state->buf, i);
state->offset -= i + 1;
assert(state->offset >= 0);
memmove(&state->buf[0], &state->buf[i + 1], state->offset);
while (i < parser_state->offset) {
if (parser_state->buf[i] == '\r') {
fprintf(stderr, "r at %d %d\n", i, parser_state->offset);
have_state_change |= handle_line(parser_state->buf, i,
timer_state);
parser_state->offset -= i + 1;
assert(parser_state->offset >= 0);
memmove(&parser_state->buf[0], &parser_state->buf[i + 1], parser_state->offset);
break;
}
i++;
if (i == state->offset)
if (i == parser_state->offset)
goto exit;
}
}
exit:
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 <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <errno.h>
#include <stdbool.h>
#include <libserialport.h>
#include <sys/socket.h>
#include <sys/timerfd.h>
#include <unistd.h>
#include "bufsize.h"
#include "serial.h"
#include "timer.h"
#include "parse_serial.h"
#define PORT 4321
#define SERIALPORT "/dev/ttyS0"
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;
}
}
//#define SERIALPORT "/dev/ttyS0"
#define SERIALPORT "/dev/ttyUSB0"
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);
if (recv_len == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
perror("eagain");
perror("sock eagain");
return 0;
} else {
perror("recvfrom");
perror("recvfrom sock");
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 {
uint64_t 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 ret;
@ -127,7 +145,7 @@ int main(void)
return -1;
}
#define MAX_EVENTS 2
#define MAX_EVENTS 5
struct epoll_event events[MAX_EVENTS];
int epollfd = epoll_create1(0);
@ -136,6 +154,7 @@ int main(void)
return -1;
}
{
struct epoll_event ev;
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = sock;
@ -144,11 +163,58 @@ int main(void)
perror("epoll_ctl: sock");
return -1;
}
}
int timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
if (timerfd == -1) {
perror("timerfd_create");
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) {
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) {
perror("epoll_wait");
return -1;
@ -159,6 +225,19 @@ int main(void)
ret = handle_sock(sock);
if (ret == -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>
enum counter_status {
COUNTER_STOPPED,
COUNTER_RUNNING,
enum timer_status {
TIMER_STOPPED,
TIMER_RUNNING,
};
struct stopwatch_time {
int32_t whole;
int32_t fraction;
int integer_value;
int integer_digits;
int fraction_value;
int fraction_digits;
};
struct running_counter {
@ -16,7 +20,7 @@ struct running_counter {
};
struct timer_state {
enum counter_status status;
enum timer_status status;
struct running_counter counter;
struct stopwatch_time time;
};