diff --git a/Makefile b/Makefile index d017ed9..9a222b3 100644 --- a/Makefile +++ b/Makefile @@ -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') diff --git a/event.h b/event.h new file mode 100644 index 0000000..2c80916 --- /dev/null +++ b/event.h @@ -0,0 +1,4 @@ +enum command { + CMD_NOP, + CMD_STATE_TRANSFER, +}; diff --git a/parse_serial.c b/parse_serial.c index 29dca90..b35d0c6 100644 --- a/parse_serial.c +++ b/parse_serial.c @@ -1,9 +1,10 @@ #include #include #include +#include #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, - struct timer_state * timer_state) +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; + } } + + 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 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; } diff --git a/parse_serial.h b/parse_serial.h new file mode 100644 index 0000000..72af749 --- /dev/null +++ b/parse_serial.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#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); diff --git a/serial.c b/serial.c new file mode 100644 index 0000000..9139eaa --- /dev/null +++ b/serial.c @@ -0,0 +1,50 @@ +#define _DEFAULT_SOURCE 1 + +#include +#include + +#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; +} diff --git a/serial.h b/serial.h new file mode 100644 index 0000000..0a84f04 --- /dev/null +++ b/serial.h @@ -0,0 +1,3 @@ +#pragma once + +int set_terminal_attributes(int fd); diff --git a/serial_forwarder b/serial_forwarder new file mode 100755 index 0000000..355dcf9 Binary files /dev/null and b/serial_forwarder differ diff --git a/serial_forwarder.c b/serial_forwarder.c index 761f9b2..b2d75a0 100644 --- a/serial_forwarder.c +++ b/serial_forwarder.c @@ -1,79 +1,23 @@ +#include +#include +#include +#include +#include #include #include -#include -#include -#include #include -#include -#include - -#include +#include +#include +#include #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,19 +154,67 @@ int main(void) return -1; } - struct epoll_event ev; - ev.events = EPOLLIN | EPOLLET; - ev.data.fd = sock; - ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, sock, &ev); - if (ret == -1) { - perror("epoll_ctl: sock"); + { + struct epoll_event ev; + ev.events = EPOLLIN | EPOLLET; + ev.data.fd = sock; + ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, sock, &ev); + 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; } + 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); } } diff --git a/timer.h b/timer.h index e3bf079..61df870 100644 --- a/timer.h +++ b/timer.h @@ -1,13 +1,17 @@ +#pragma once + #include -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; };