diff options
| author | Xavier Del Campo Romero <xavi.dcr@tutanota.com> | 2022-09-27 17:03:06 +0200 |
|---|---|---|
| committer | Xavier Del Campo Romero <xavi.dcr@tutanota.com> | 2022-11-01 16:26:16 +0100 |
| commit | 980858186149651df5543b6fc99a4f7db0cdd089 (patch) | |
| tree | d347200b0a562d84df505097651ad0642f207fdd /src/net/posix | |
| parent | 39f50e601d395bbd2d78d0147ac530b756da2fff (diff) | |
| download | jancity-980858186149651df5543b6fc99a4f7db0cdd089.tar.gz | |
WIP
Diffstat (limited to 'src/net/posix')
| -rw-r--r-- | src/net/posix/src/serial.c | 276 |
1 files changed, 263 insertions, 13 deletions
diff --git a/src/net/posix/src/serial.c b/src/net/posix/src/serial.c index b0357b9..852fa0b 100644 --- a/src/net/posix/src/serial.c +++ b/src/net/posix/src/serial.c @@ -1,40 +1,290 @@ #include <net.h> #include <net/serial.h> #include <net_private.h> +#include <transport.h> +#include <fcntl.h> +#include <termios.h> +#include <unistd.h> +#include <errno.h> #include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> -int net_read_serial(struct net_socket_domain *const h, void *const buf, - const size_t n) +/* Based on https://en.wikibooks.org/wiki/Serial_Programming/termios */ + +struct net_host_domain { - return -1; + int fd; + enum net_role role; + struct transport_handle h; + struct net_event ev; +}; + +int net_write_serial(struct net_host_domain *const h, const net_peer p, + const void *const buf, const size_t n) +{ + return transport_send(&h->h, buf, n); } -int net_write_serial(struct net_socket_domain *const h, const void *const buf, - const size_t n) +int net_close_serial(struct net_host_domain *const h) { - return -1; + if (h && h->fd >= 0) + close(h->fd); + + free(h); + return 0; } -int net_close_serial(struct net_socket_domain *const h) +int net_update_serial(struct net_host_domain *const h) { - return -1; + return transport_update(&h->h); +} + +static void on_received(const struct transport_event *const ev, + void *const arg) +{ + struct net_host_domain *const h = arg; + + net_serial_on_received(ev, &h->ev); +} + +static int on_read(void *const buf, const size_t n, void *const arg) +{ + struct net_host_domain *const h = arg; + const ssize_t res = read(h->fd, buf, n); + + if (res < 0) + switch (errno) + { + case EAGAIN: + /* Fall through. */ + case EINTR: + return 0; + + default: + return -1; + } + + return res; +} + +static int on_write(const void *const buf, const size_t n, void *const arg) +{ + struct net_host_domain *const h = arg; + const ssize_t res = write(h->fd, buf, n); + + if (res < 0) + switch (errno) + { + case EAGAIN: + /* Fall through. */ + case EINTR: + return 0; + + default: + return -1; + } + + return res; } -int net_update_serial(struct net_socket_domain *const h) +static struct transport_cfg get_transport_cfg(void *const arg) { + return (const struct transport_cfg) + { + .arg = arg, + .received = on_received, + .read = on_read, + .write = on_write + }; +} + +static int open_dev(const char *const dev) +{ + return open(dev, O_RDWR | O_NOCTTY | O_NONBLOCK); +} + +static int get_speed(const unsigned long baud, speed_t *const out_s) +{ +#define BAUD(x) {.baud = x, .s = B##x} + + static const struct speed + { + unsigned long baud; + speed_t s; + } speeds[] = + { + BAUD(19200), + BAUD(38400), + BAUD(57600), + BAUD(115200) + }; + +#undef BAUD + + for (size_t i = 0; i < sizeof speeds / sizeof *speeds; i++) + { + const struct speed *const s = &speeds[i]; + + if (baud == s->baud) + { + *out_s = s->s; + return 0; + } + } + return -1; } -struct net_socket_domain *net_connect_serial(const union net_connect *const srv) +static int config_fd(const int fd, const unsigned long baud) +{ + struct termios t; + int ret = -1; + + if (tcgetattr(fd, &t)) + { + fprintf(stderr, "%s: tcgetattr(3) failed: %s\n", __func__, + strerror(errno)); + goto end; + } + + /* cfmakeraw(3) is non-portable, but its man page states the + * following attributes are set. */ + t.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP + | INLCR | IGNCR | ICRNL | IXON); + t.c_oflag &= ~OPOST; + t.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + t.c_cflag &= ~(CSIZE | PARENB); + t.c_cflag |= CS8; + + /* Make read(2) immediately regardless how many data is available. */ + t.c_cc[VMIN] = 0; + t.c_cc[VTIME] = 0; + + speed_t s; + + if (get_speed(baud, &s)) + { + fprintf(stderr, "%s: get_speed failed\n", __func__); + goto end; + } + else if (cfsetispeed(&t, s)) + { + fprintf(stderr, "%s: cfsetispeed(3) failed: %s\n", __func__, + strerror(errno)); + goto end; + } + else if (cfsetospeed(&t, s)) + { + fprintf(stderr, "%s: cfsetospeed(3) failed: %s\n", __func__, + strerror(errno)); + goto end; + } + else if (tcsetattr(fd, TCSANOW, &t)) + { + fprintf(stderr, "%s: tcsetattr(3) failed: %s\n", __func__, + strerror(errno)); + goto end; + } + + ret = 0; + +end: + return ret; +} + +struct net_host_domain *net_connect_serial(const union net_connect *const c) { + struct net_host_domain *const h = malloc(sizeof *h); + const struct net_serial_cfg *const cfg = &c->serial.cfg; + + if (!h) + goto failure; + + *h = (const struct net_host_domain) + { + .role = NET_ROLE_CLIENT, + .fd = open_dev(cfg->dev), + .h = + { + .cfg = get_transport_cfg(h) + } + }; + + if (h->fd < 0) + { + fprintf(stderr, "%s: open(2) failed: %s\n", __func__, + strerror(errno)); + goto failure; + } + else if (config_fd(h->fd, cfg->baud)) + { + fprintf(stderr, "%s: config_gf failed\n", __func__); + goto failure; + } + else if (transport_connect(&h->h)) + { + fprintf(stderr, "%s: transport_connect failed\n", __func__); + goto failure; + } + + return h; + +failure: + net_close_serial(h); return NULL; } -struct net_socket_domain *net_server_serial(const union net_server *const srv) +int net_set_event_serial(struct net_host_domain *const h, + const struct net_event *const ev) +{ + h->ev = *ev; + return 0; +} + +struct net_host_domain *net_server_serial(const union net_server *const s) { + struct net_host_domain *const h = malloc(sizeof *h); + const struct net_serial_cfg *const cfg = &s->serial.cfg; + + if (!h) + goto failure; + + *h = (const struct net_host_domain) + { + .role = NET_ROLE_SERVER, + .fd = open_dev(cfg->dev), + .h = + { + .cfg = get_transport_cfg(h) + } + }; + + if (h->fd < 0) + { + fprintf(stderr, "%s: open(2) failed: %s\n", __func__, + strerror(errno)); + goto failure; + } + else if (config_fd(h->fd, cfg->baud)) + { + fprintf(stderr, "%s: config_gf failed\n", __func__); + goto failure; + } + + return h; + +failure: + net_close_serial(h); return NULL; } +enum net_role net_role_serial(const struct net_host_domain *const h) +{ + return h->role; +} + int net_init_serial(void) { return 0; @@ -44,7 +294,7 @@ void net_deinit_serial(void) { } -const char *const *net_serial_devices(size_t *const n) +bool net_serial_unique(void) { - return NULL; + return false; } |
