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 | |
| parent | 39f50e601d395bbd2d78d0147ac530b756da2fff (diff) | |
| download | jancity-980858186149651df5543b6fc99a4f7db0cdd089.tar.gz | |
WIP
Diffstat (limited to 'src/net')
| -rw-r--r-- | src/net/CMakeLists.txt | 5 | ||||
| -rw-r--r-- | src/net/inc/net.h | 65 | ||||
| -rw-r--r-- | src/net/inc/net/serial.h | 4 | ||||
| -rw-r--r-- | src/net/posix/src/serial.c | 276 | ||||
| -rw-r--r-- | src/net/privinc/net_private.h | 34 | ||||
| -rw-r--r-- | src/net/privinc/net_serial_private.h | 19 | ||||
| -rw-r--r-- | src/net/ps1/src/net.c | 238 | ||||
| -rw-r--r-- | src/net/src/enet/ipv4.c | 111 | ||||
| -rw-r--r-- | src/net/src/net.c | 99 | ||||
| -rw-r--r-- | src/net/src/serial.c | 37 | ||||
| -rw-r--r-- | src/net/win9x/src/serial.c | 404 |
11 files changed, 1132 insertions, 160 deletions
diff --git a/src/net/CMakeLists.txt b/src/net/CMakeLists.txt index 79bd653..03329c9 100644 --- a/src/net/CMakeLists.txt +++ b/src/net/CMakeLists.txt @@ -1,4 +1,6 @@ -set(src "src/common.c") +set(src "src/common.c" "src/serial.c") +set(priv_deps transport) +set(privinc "privinc") if(PS1_BUILD) set(src ${src} @@ -24,7 +26,6 @@ else() endif() set(priv_deps ${priv_deps} util ENET) - set(privinc ${privinc} "privinc") endif() add_library(net ${src}) diff --git a/src/net/inc/net.h b/src/net/inc/net.h index bbbd4ce..47bd0f8 100644 --- a/src/net/inc/net.h +++ b/src/net/inc/net.h @@ -3,7 +3,6 @@ #include <stdbool.h> #include <stddef.h> -#include <stdint.h> #ifdef __cplusplus extern "C" @@ -16,39 +15,54 @@ enum net_domain NET_DOMAIN_SERIAL }; +enum net_role +{ + NET_ROLE_SERVER, + NET_ROLE_CLIENT +}; + +typedef void *net_peer; + +struct net_event +{ + void (*connected)(net_peer p, void *arg); + void (*disconnected)(net_peer p, void *arg); + void (*received)(net_peer p, const void *buf, size_t n, void *arg); + void *arg; +}; + union net_connect { struct net_connect_common { enum net_domain domain; - - struct net_connect_ev - { - void (*connected)(void *arg); - void (*disconnected)(void *arg); - void *arg; - } ev; + struct net_event ev; } common; struct net_connect_ipv4 { struct net_connect_common common; const char *addr; - uint16_t port; + short port; } ipv4; struct net_connect_serial { struct net_connect_common common; - const char *dev; - unsigned long baud; - enum + struct net_serial_cfg { - NET_PARITY_NONE, - NET_PARITY_ODD, - NET_PARITY_EVEN - } parity; + const char *dev; + unsigned long baud; + bool hw_ctrl; + + enum + { + NET_PARITY_NONE, + NET_PARITY_ODD, + NET_PARITY_EVEN + } parity; + } cfg; } serial; }; @@ -58,31 +72,34 @@ union net_server { enum net_domain domain; unsigned max_players; + struct net_event ev; } common; struct net_server_ipv4 { struct net_server_common common; - uint16_t port; + short port; } ipv4; struct net_server_serial { struct net_server_common common; + struct net_serial_cfg cfg; } serial; }; -struct net_socket; +struct net_host; int net_init(void); void net_deinit(void); -int net_update(struct net_socket *s); +int net_update(struct net_host *s); bool net_available(enum net_domain d); -struct net_socket *net_server(const union net_server *srv); -struct net_socket *net_connect(const union net_connect *c); -int net_read(struct net_socket *s, void *buf, size_t n); -int net_write(struct net_socket *s, const void *buf, size_t n); -int net_close(struct net_socket *s); +struct net_host *net_server(const union net_server *srv); +struct net_host *net_connect(const union net_connect *c); +int net_write(struct net_host *h, net_peer p, const void *buf, size_t n); +int net_set_event(struct net_host *h, const struct net_event *ev); +int net_close(struct net_host *s); +enum net_role net_role(const struct net_host *s); const char *net_domain_str(enum net_domain d); #ifdef __cplusplus diff --git a/src/net/inc/net/serial.h b/src/net/inc/net/serial.h index ee81e9f..06cbc95 100644 --- a/src/net/inc/net/serial.h +++ b/src/net/inc/net/serial.h @@ -1,14 +1,14 @@ #ifndef NET_SERIAL_H #define NET_SERIAL_H -#include <stddef.h> +#include <stdbool.h> #ifdef __cplusplus extern "C" { #endif -const char *const *net_serial_devices(size_t *n); +bool net_serial_unique(void); #ifdef __cplusplus } 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; } diff --git a/src/net/privinc/net_private.h b/src/net/privinc/net_private.h index 53d7cd1..bc405d1 100644 --- a/src/net/privinc/net_private.h +++ b/src/net/privinc/net_private.h @@ -2,29 +2,35 @@ #define NET_PRIVATE_H #include <net.h> +#include <transport.h> +#include <stddef.h> -struct net_socket +struct net_host { enum net_domain d; - struct net_socket_domain *s; + struct net_host_domain *h; }; int net_init_ipv4(void); void net_deinit_ipv4(void); -struct net_socket_domain *net_server_ipv4(const union net_server *srv); -struct net_socket_domain *net_connect_ipv4(const union net_connect *c); -int net_read_ipv4(struct net_socket_domain *h, void *buf, size_t n); -int net_write_ipv4(struct net_socket_domain *h, const void *buf, size_t n); -int net_close_ipv4(struct net_socket_domain *h); -int net_update_ipv4(struct net_socket_domain *h); +struct net_host_domain *net_server_ipv4(const union net_server *srv); +struct net_host_domain *net_connect_ipv4(const union net_connect *c); +int net_write_ipv4(struct net_host_domain *h, net_peer p, const void *buf, size_t n); +int net_close_ipv4(struct net_host_domain *h); +int net_update_ipv4(struct net_host_domain *h); +enum net_role net_role_ipv4(const struct net_host_domain *h); +int net_set_event_ipv4(struct net_host_domain *d, const struct net_event *ev); int net_init_serial(void); void net_deinit_serial(void); -struct net_socket_domain *net_server_serial(const union net_server *srv); -struct net_socket_domain *net_connect_serial(const union net_connect *c); -int net_read_serial(struct net_socket_domain *h, void *buf, size_t n); -int net_write_serial(struct net_socket_domain *h, const void *buf, size_t n); -int net_close_serial(struct net_socket_domain *h); -int net_update_serial(struct net_socket_domain *h); +struct net_host_domain *net_server_serial(const union net_server *srv); +struct net_host_domain *net_connect_serial(const union net_connect *c); +int net_write_serial(struct net_host_domain *h, net_peer p, const void *buf, size_t n); +int net_close_serial(struct net_host_domain *h); +int net_update_serial(struct net_host_domain *h); +enum net_role net_role_serial(const struct net_host_domain *h); +int net_set_event_serial(struct net_host_domain *d, const struct net_event *ev); +void net_serial_on_received(const struct transport_event *t_ev, + const struct net_event *n_ev); #endif /* NET_PRIVATE_H */ diff --git a/src/net/privinc/net_serial_private.h b/src/net/privinc/net_serial_private.h new file mode 100644 index 0000000..74eee55 --- /dev/null +++ b/src/net/privinc/net_serial_private.h @@ -0,0 +1,19 @@ +#ifndef NET_SERIAL_PRIVATE_H +#define NET_SERIAL_PRIVATE_H + +#include <net.h> +#include <transport.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +void net_serial_on_received(const struct transport_event *t_ev, + const struct net_event *n_ev); + +#ifdef __cplusplus +} +#endif + +#endif /* NET_SERIAL_PRIVATE_H */ diff --git a/src/net/ps1/src/net.c b/src/net/ps1/src/net.c index 8c037f3..af964cf 100644 --- a/src/net/ps1/src/net.c +++ b/src/net/ps1/src/net.c @@ -1,39 +1,233 @@ #include <net.h> #include <net/serial.h> -#include <errno.h> +#include <net_serial_private.h> +#include <transport.h> +#include <psx.h> +#include <psxsio.h> #include <stdbool.h> #include <stddef.h> +#include <stdio.h> -int net_read(struct net_socket *const h, void *const buf, const size_t n) +static struct net_host { - return -1; + enum net_role role; + bool used, sending; + struct transport_handle h; + struct net_event ev; + + struct net_host_fifo + { + char buf[32]; + size_t pending, read; + } in, out; +} host; + +enum {TX_INT_ENABLE = 1 << 10}; + +int net_write(struct net_host *const h, const net_peer peer, + const void *const buf, size_t n) +{ + return transport_send(&h->h, buf, n); } -int net_write(struct net_socket *const h, const void *const buf, const size_t n) +int net_close(struct net_host *const h) { - return -1; + SIOStop(); + RemoveSIOHandler(); + h->used = false; + return 0; +} + +static void send_byte(struct net_host *const h) +{ + struct net_host_fifo *const f = &h->out; + + if (f->pending != f->read) + { + if (SIOCheckOutBuffer()) + { + size_t new = f->read + 1; + + if (new >= sizeof f->buf) + new = 0; + + h->sending = true; + SIOSendByte(f->buf[f->read = new]); + } + } + else + { + h->sending = false; + SIO_CTRL &= ~TX_INT_ENABLE; + } } -int net_close(struct net_socket *const h) +int net_update(struct net_host *const h) { - return -1; + return transport_update(&h->h); } -int net_update(struct net_socket *const h) +static void on_received(const struct transport_event *const ev, + void *const arg) { - return -1; + struct net_host *const h = arg; + + net_serial_on_received(ev, &h->ev); } -struct net_socket *net_connect(const union net_connect *const c) +static int on_write(const void *const buf, size_t n, void *const arg) { - return NULL; + const size_t orig = n; + struct net_host *const h = arg; + + EnterCriticalSection(); + + for (const char *b = buf; n; n--, b++) + { + struct net_host_fifo *const f = &h->out; + size_t new = f->pending + 1; + + if (new >= sizeof f->buf) + new = 0; + + if (new == f->read) + { + fprintf(stderr, "%s: out FIFO full\n", __func__); + goto end; + } + + f->buf[f->pending = new] = *b; + } + + if (n != orig) + SIO_CTRL |= TX_INT_ENABLE; + +end: + ExitCriticalSection(); + return orig - n; +} + +static int on_read(void *const buf, const size_t n, void *const arg) +{ + size_t rem = n; + struct net_host *const h = arg; + struct net_host_fifo *const f = &h->in; + + EnterCriticalSection(); + + for (char *b = buf; rem; rem--, b++) + { + if (f->read == f->pending) + goto end; + + size_t new = f->read + 1; + + if (new >= sizeof f->buf) + new = 0; + + *b = f->buf[f->read = new]; + } + +end: + ExitCriticalSection(); + return n - rem; +} + +static struct transport_cfg get_transport_cfg(void) +{ + return (const struct transport_cfg) + { + .arg = &host, + .received = on_received, + .read = on_read, + .write = on_write + }; } -struct net_socket *net_server(const union net_server *const c) +static void on_byte_received(struct net_host *const h, const char ch) { + struct net_host_fifo *const f = &h->in; + size_t new = f->pending + 1; + + if (new >= sizeof f->buf) + new = 0; + + f->buf[f->pending = new] = ch; +} + +static void sio(void) +{ + struct net_host *const h = &host; + + if (SIOCheckInBuffer()) + on_byte_received(h, SIOReadByte()); + + send_byte(h); +} + +static void start_sio(const unsigned long baud) +{ + enum + { + RX_INT_MODE_1_BYTE = 1 << 8, + RX_INT_ENABLE = 1 << 11 + }; + + SIOStartEx(baud, SIO_DATA_LEN_8, SIO_PARITY_NONE, SIO_STOP_BIT_1); + SetSIOHandler(sio); + SIO_CTRL |= RX_INT_ENABLE | RX_INT_MODE_1_BYTE; +} + +struct net_host *net_connect(const union net_connect *const c) +{ + struct net_host *const h = &host; + + if (h->used) + goto failure; + + start_sio(c->serial.cfg.baud); + + *h = (const struct net_host) + { + .role = NET_ROLE_CLIENT, + .used = true, + .h = + { + .cfg = get_transport_cfg() + } + }; + + if (transport_connect(&h->h)) + goto failure; + + return h; + +failure: + net_close(h); return NULL; } +struct net_host *net_server(const union net_server *const c) +{ + struct net_host *const h = &host; + + if (h->used) + return NULL; + + *h = (const struct net_host) + { + .role = NET_ROLE_SERVER, + .used = true, + .h = + { + .cfg = get_transport_cfg() + } + }; + + start_sio(c->serial.cfg.baud); + return h; +} + int net_init(void) { return 0; @@ -43,18 +237,24 @@ void net_deinit(void) { } +int net_set_event(struct net_host *const h, + const struct net_event *const ev) +{ + h->ev = *ev; + return 0; +} + bool net_available(const enum net_domain d) { return d == NET_DOMAIN_SERIAL; } -const char *const *net_serial_devices(size_t *const n) +bool net_serial_unique(void) { - static const char *const dev[] = - { - "PS1 port" - }; + return true; +} - *n = sizeof dev / sizeof *dev; - return dev; +enum net_role net_role(const struct net_host *s) +{ + return s->role; } diff --git a/src/net/src/enet/ipv4.c b/src/net/src/enet/ipv4.c index 86d1697..53b6dc0 100644 --- a/src/net/src/enet/ipv4.c +++ b/src/net/src/enet/ipv4.c @@ -5,11 +5,11 @@ #include <stdio.h> #include <stdlib.h> -struct net_socket_domain +struct net_host_domain { + enum net_role role; ENetHost *host; - ENetPeer *peers; - struct net_connect_ev ev; + struct net_event ev; }; enum @@ -18,17 +18,20 @@ enum MAX_CHANNELS }; -struct net_socket_domain *net_connect_ipv4(const union net_connect *const c) +struct net_host_domain *net_connect_ipv4(const union net_connect *const c) { - struct net_socket_domain *const s = calloc(1, sizeof *s); + struct net_host_domain *const h = malloc(sizeof *h); - if (!s) + if (!h) goto failure; - s->ev = c->common.ev; - s->host = enet_host_create(NULL, 1, MAX_CHANNELS, 0, 0); + *h = (const struct net_host_domain) + { + .ev = c->common.ev, + .host = enet_host_create(NULL, 1, MAX_CHANNELS, 0, 0) + }; - if (!s->host) + if (!h->host) { fprintf(stderr, "%s: enet_host_create failed\n", __func__); goto failure; @@ -44,66 +47,89 @@ struct net_socket_domain *net_connect_ipv4(const union net_connect *const c) addr.port = c->ipv4.port; - if (!(s->peers = enet_host_connect(s->host, &addr, MAX_CHANNELS, 0))) + if (!enet_host_connect(h->host, &addr, MAX_CHANNELS, 0)) { fprintf(stderr, "%s: enet_host_connect failed\n", __func__); goto failure; } - return s; + h->role = NET_ROLE_CLIENT; + return h; failure: - if (s && s->host) - enet_host_destroy(s->host); + if (h && h->host) + enet_host_destroy(h->host); - free(s); + free(h); return NULL; } -int net_read_ipv4(struct net_socket_domain *const s, void *const buf, - const size_t n) +int net_write_ipv4(struct net_host_domain *const h, const net_peer p, + const void *const buf, const size_t n) { - return -1; + ENetPacket *const pk = enet_packet_create(buf, n, ENET_PACKET_FLAG_RELIABLE); + + if (!p) + { + fprintf(stderr, "%s: enet_packet_create failed\n", __func__); + return -1; + } + else if (enet_peer_send(p, CHANNEL, pk)) + { + fprintf(stderr, "%s: enet_peer_send failed\n", __func__); + return -1; + } + + return 0; } -int net_write_ipv4(struct net_socket_domain *const s, const void *const buf, - const size_t n) +int net_set_event_ipv4(struct net_host_domain *const d, + const struct net_event *const ev) { - return -1; + d->ev = *ev; + return 0; } -int net_close_ipv4(struct net_socket_domain *const s) +int net_close_ipv4(struct net_host_domain *const h) { - if (s && s->host) - enet_host_destroy(s->host); + if (h && h->host) + enet_host_destroy(h->host); - free(s); + free(h); return 0; } -int net_update_ipv4(struct net_socket_domain *const s) +int net_update_ipv4(struct net_host_domain *const h) { int res; ENetEvent ev; - while ((res = enet_host_service(s->host, &ev, 0)) > 0) + while ((res = enet_host_service(h->host, &ev, 0)) > 0) { switch (ev.type) { case ENET_EVENT_TYPE_CONNECT: - if (s->ev.connected) - s->ev.connected(s->ev.arg); + if (h->ev.connected) + h->ev.connected(ev.peer, h->ev.arg); break; case ENET_EVENT_TYPE_DISCONNECT: - if (s->ev.disconnected) - s->ev.disconnected(s->ev.arg); + if (h->ev.disconnected) + h->ev.disconnected(ev.peer, h->ev.arg); break; case ENET_EVENT_TYPE_RECEIVE: + if (h->ev.received) + { + const enet_uint8 *const buf = ev.packet->data; + const size_t n = ev.packet->dataLength; + + if (buf && n) + h->ev.received(ev.peer, buf, n, h->ev.arg); + } break; case ENET_EVENT_TYPE_NONE: @@ -120,11 +146,11 @@ int net_update_ipv4(struct net_socket_domain *const s) return 0; } -struct net_socket_domain *net_server_ipv4(const union net_server *const srv) +struct net_host_domain *net_server_ipv4(const union net_server *const srv) { - struct net_socket_domain *const s = calloc(1, sizeof *s); + struct net_host_domain *const h = malloc(sizeof *h); - if (!s) + if (!h) goto failure; const ENetAddress addr = @@ -132,22 +158,31 @@ struct net_socket_domain *net_server_ipv4(const union net_server *const srv) .port = srv->ipv4.port }; - s->host = enet_host_create(&addr, srv->common.max_players, - MAX_CHANNELS, 0, 0); + *h = (const struct net_host_domain) + { + .host = enet_host_create(&addr, srv->common.max_players, + MAX_CHANNELS, 0, 0) + }; - if (!s->host) + if (!h->host) { fprintf(stderr, "%s: enet_host_create failed\n", __func__); goto failure; } - return s; + h->role = NET_ROLE_SERVER; + return h; failure: - free(s); + free(h); return NULL; } +enum net_role net_role_ipv4(const struct net_host_domain *const h) +{ + return h->role; +} + int net_init_ipv4(void) { return enet_initialize(); diff --git a/src/net/src/net.c b/src/net/src/net.c index f5dc938..fb0d155 100644 --- a/src/net/src/net.c +++ b/src/net/src/net.c @@ -1,104 +1,129 @@ #include <net.h> #include <net_private.h> +#include <net_serial_private.h> #include <stddef.h> #include <stdlib.h> -struct net_socket *net_connect(const union net_connect *const c) +struct net_host *net_connect(const union net_connect *const c) { - struct net_socket *const s = calloc(1, sizeof *s); + struct net_host *const h = malloc(sizeof *h); - if (!s) + if (!h) goto failure; - static struct net_socket_domain *(*const f[])(const union net_connect *) = + static struct net_host_domain *(*const f[])(const union net_connect *) = { [NET_DOMAIN_IPV4] = net_connect_ipv4, [NET_DOMAIN_SERIAL] = net_connect_serial }; - if (!(s->s = f[c->common.domain](c))) + *h = (const struct net_host) + { + .d = c->common.domain + }; + + if (!(h->h = f[h->d](c))) goto failure; - return s; + return h; failure: - free(s); + free(h); return NULL; } -int net_read(struct net_socket *const s, void *const buf, const size_t n) +int net_write(struct net_host *const h, const net_peer p, + const void *const buf, const size_t n) { - static int (*const f[])(struct net_socket_domain *, void *, size_t) = - { - [NET_DOMAIN_IPV4] = net_read_ipv4, - [NET_DOMAIN_SERIAL] = net_read_serial - }; - - return f[s->d](s->s, buf, n); -} - -int net_write(struct net_socket *const s, const void *const buf, const size_t n) -{ - static int (*const f[])(struct net_socket_domain *, const void *, size_t) = + static int (*const f[])(struct net_host_domain *, net_peer, + const void *, size_t) = { [NET_DOMAIN_IPV4] = net_write_ipv4, [NET_DOMAIN_SERIAL] = net_write_serial }; - return f[s->d](s->s, buf, n); + return f[h->d](h->h, p, buf, n); } -int net_close(struct net_socket *const s) +int net_close(struct net_host *const h) { - if (!s) + if (!h) return 0; - static int (*const f[])(struct net_socket_domain *) = + static int (*const f[])(struct net_host_domain *) = { [NET_DOMAIN_IPV4] = net_close_ipv4, [NET_DOMAIN_SERIAL] = net_close_serial }; - const int res = f[s->d](s->s); + const int res = f[h->d](h->h); - free(s); + free(h); return res; } -int net_update(struct net_socket *const s) +int net_update(struct net_host *const h) { - static int (*const f[])(struct net_socket_domain *) = + static int (*const f[])(struct net_host_domain *) = { [NET_DOMAIN_IPV4] = net_update_ipv4, [NET_DOMAIN_SERIAL] = net_update_serial }; - return f[s->d](s->s); + return f[h->d](h->h); } -struct net_socket *net_server(const union net_server *const srv) +struct net_host *net_server(const union net_server *const srv) { - struct net_socket *const s = calloc(1, sizeof *s); + struct net_host *const h = malloc(sizeof *h); - if (!s) + if (!h) goto failure; - s->d = srv->common.domain; - - static struct net_socket_domain *(*const f[])(const union net_server *) = + static struct net_host_domain *(*const f[])(const union net_server *) = { [NET_DOMAIN_IPV4] = net_server_ipv4, [NET_DOMAIN_SERIAL] = net_server_serial }; - if (!(s->s = f[s->d](srv))) + *h = (const struct net_host) + { + .d = srv->common.domain + }; + + if (!(h->h = f[h->d](srv))) goto failure; + return h; + failure: - net_close(s); + net_close(h); return NULL; } +enum net_role net_role(const struct net_host *const h) +{ + static enum net_role (*const f[])(const struct net_host_domain *) = + { + [NET_DOMAIN_IPV4] = net_role_ipv4, + [NET_DOMAIN_SERIAL] = net_role_serial + }; + + return f[h->d](h->h); +} + +int net_set_event(struct net_host *const h, + const struct net_event *const ev) +{ + static int (*const f[])(struct net_host_domain *, const struct net_event *) = + { + [NET_DOMAIN_IPV4] = net_set_event_ipv4, + [NET_DOMAIN_SERIAL] = net_set_event_serial + }; + + return f[h->d](h->h, ev); +} + int net_init(void) { return net_init_ipv4() || net_init_serial(); diff --git a/src/net/src/serial.c b/src/net/src/serial.c new file mode 100644 index 0000000..a9f3f73 --- /dev/null +++ b/src/net/src/serial.c @@ -0,0 +1,37 @@ +#include <net.h> +#include <net_serial_private.h> +#include <transport.h> + +void net_serial_on_received(const struct transport_event *const t_ev, + const struct net_event *const n_ev) +{ + static struct peer + { + int dummy; + } peer; + + switch (t_ev->common.type) + { + case TRANSPORT_EVENT_TYPE_CONNECT: + if (n_ev->connected) + n_ev->connected(&peer, n_ev->arg); + + break; + + case TRANSPORT_EVENT_TYPE_DISCONNECT: + if (n_ev->disconnected) + n_ev->disconnected(&peer, n_ev->arg); + + break; + + case TRANSPORT_EVENT_TYPE_DATA: + if (n_ev->received) + { + const struct transport_event_data *const d = &t_ev->u.data; + + n_ev->received(&peer, d->buf, d->n, n_ev->arg); + } + + break; + } +} diff --git a/src/net/win9x/src/serial.c b/src/net/win9x/src/serial.c index c12369a..c1cffb6 100644 --- a/src/net/win9x/src/serial.c +++ b/src/net/win9x/src/serial.c @@ -1,36 +1,406 @@ #include <net.h> #include <net_private.h> +#include <windows.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) +struct net_host_domain { - return -1; + HANDLE com; + enum net_role role; + struct transport_handle h; + struct net_event ev; + + struct req + { + const void *buf; + size_t n; + OVERLAPPED ov; + + enum req_type + { + REQ_TYPE_READ, + REQ_TYPE_WRITE + } type; + } *reqs; + + size_t n_reqs; +}; + +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_close_serial(struct net_host_domain *const h) +{ + if (h) + { + if (h->com != INVALID_HANDLE_VALUE) + CloseHandle(h->com); + + if (h->reqs) + for (size_t i = 0; i < h->n_reqs; i++) + { + struct req *const r = &h->reqs[i]; + + if (r->ov.hEvent) + CloseHandle(r->ov.hEvent); + } + + free(h->reqs); + } + + free(h); + return 0; +} + +int net_update_serial(struct net_host_domain *const h) +{ + 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 struct req *find_req(struct net_host_domain *const h, + const void *const buf, const size_t n, const enum req_type t) +{ + for (size_t i = 0; i < h->n_reqs; i++) + { + struct req *const r = &h->reqs[i]; + + if (r->buf == buf && r->n == n && r->type == t) + return r; + } + + return NULL; } -int net_write_serial(struct net_socket_domain *const h, const void *const buf, - const size_t n) +static int free_req(struct net_host_domain *const h, struct req *const r) { + for (size_t i = 0; i < h->n_reqs; i++) + if (&h->reqs[i] == r) + { + const HANDLE ev = r->ov.hEvent; + + if (ev) + CloseHandle(ev); + + for (size_t j = i + 1; j < h->n_reqs; j++) + h->reqs[j - 1] = h->reqs[j]; + + const size_t n = h->n_reqs - 1; + + if (n) + { + if (!(h->reqs = realloc(h->reqs, n * sizeof *h->reqs))) + { + fprintf(stderr, "%s: realloc(3) failed: %s\n", + __func__, strerror(errno)); + return -1; + } + } + else + { + free(h->reqs); + h->reqs = NULL; + } + + h->n_reqs = n; + return 0; + } + return -1; } -int net_close_serial(struct net_socket_domain *const h) +static struct req *alloc_req(struct net_host_domain *const h, + const void *const buf, const size_t n, const enum req_type t) +{ + if (!(h->reqs = realloc(h->reqs, (h->n_reqs + 1) * sizeof *h->reqs))) + { + fprintf(stderr, "%s: realloc(3) failed: %s\n", + __func__, strerror(errno)); + return NULL; + } + + struct req *const r = &h->reqs[h->n_reqs++]; + + *r = (const struct req) + { + .buf = buf, + .n = n, + .type = t, + .ov = + { + .hEvent = CreateEvent(NULL, TRUE, FALSE, NULL) + } + }; + + if (!r->ov.hEvent) + { + fprintf(stderr, "%s: CreateEvent failed: %#lx\n", __func__, + GetLastError()); + free_req(h, r); + return NULL; + } + + return r; +} + +static int get_result(struct net_host_domain *const h, struct req *const r) +{ + DWORD res; + + if (GetOverlappedResult(h->com, &r->ov, &res, FALSE) == FALSE) + { + const DWORD error = GetLastError(); + + if (error != ERROR_IO_INCOMPLETE) + { + fprintf(stderr, "%s: GetOverlappedResult failed: %#lx\n", + __func__, error); + return -1; + } + else + return 0; + } + else if (ResetEvent(r->ov.hEvent) == FALSE) + { + fprintf(stderr, "%s: ResetEvent failed: %#lx\n", __func__, + GetLastError()); + return -1; + } + else if (free_req(h, r)) + { + fprintf(stderr, "%s: free_req failed\n", __func__); + return -1; + } + + return res; +} + +static int on_read(void *const buf, const size_t n, void *const arg) { + const enum req_type t = REQ_TYPE_READ; + struct net_host_domain *const h = arg; + struct req *r = find_req(h, buf, n, t); + + if (!r) + { + DWORD res; + + if (!(r = alloc_req(h, buf, n, t))) + { + fprintf(stderr, "%s: alloc_req failed\n", __func__); + goto failure; + } + else if (ReadFile(h->com, buf, n, &res, &r->ov) == FALSE + && GetLastError() != ERROR_IO_PENDING) + { + fprintf(stderr, "%s: ReadFile failed: %#lx\n", + __func__, GetLastError()); + goto failure; + } + } + + return get_result(h, r); + +failure: + if (r) + free_req(h, r); + return -1; } -int net_update_serial(struct net_socket_domain *const h) +static int on_write(const void *const buf, const size_t n, void *const arg) { + const enum req_type t = REQ_TYPE_WRITE; + struct net_host_domain *const h = arg; + struct req *r = find_req(h, buf, n, t); + + if (!r) + { + DWORD res; + + if (!(r = alloc_req(h, buf, n, t))) + { + fprintf(stderr, "%s: alloc_req failed\n", __func__); + goto failure; + } + else if (WriteFile(h->com, buf, n, &res, &r->ov) == FALSE + && GetLastError() != ERROR_IO_PENDING) + { + fprintf(stderr, "%s: WriteFile failed: %#lx\n", + __func__, GetLastError()); + goto failure; + } + } + + return get_result(h, r); + +failure: + if (r) + free_req(h, r); + return -1; } -struct net_socket_domain *net_connect_serial(const union net_connect *const srv) +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 HANDLE init_com(const char *const dev) +{ + static const char *const prefix = "\\\\.\\"; + + /* snprintf(NULL, 0, ...) could have been used to determine the + * total length of the string. However, this is a C99 feature that + * must be supported by the underlying libc, which is not the case + * for Win9x, where only ANSI C is supported. */ + char *const fulldev = calloc(1, strlen(prefix) + strlen(dev) + 1); + HANDLE ret = INVALID_HANDLE_VALUE; + + if (!fulldev) + { + fprintf(stderr, "%s: calloc(3) failed: %s\n", __func__, + strerror(errno)); + goto end; + } + + strcat(fulldev, prefix); + strcat(fulldev, dev); + + ret = CreateFile( + fulldev, + GENERIC_READ | GENERIC_WRITE, + 0, /* No sharing. */ + NULL, /* No security. */ + OPEN_EXISTING, /* Open existing port only. */ + FILE_FLAG_OVERLAPPED, /* Asynchronous I/O. */ + NULL /* NULL for COM devices. */); + + if (ret == INVALID_HANDLE_VALUE) + fprintf(stderr, "%s: CreateFile failed: %#lx\n", __func__, + GetLastError()); + +end: + free(fulldev); + return ret; +} + +static int config_baud(const HANDLE com, const unsigned long baud) +{ + DCB dcb = + { + .DCBlength = sizeof dcb, + .BaudRate = baud, + .ByteSize = 8, + .StopBits = ONESTOPBIT, + .Parity = NOPARITY, + .fBinary = TRUE + }; + + if (SetCommState(com, &dcb) == FALSE) + { + fprintf(stderr, "%s: SetCommState failed: %#lx\n", + __func__, GetLastError()); + return -1; + } + + return 0; +} + +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, + .com = init_com(cfg->dev), + .h = + { + .cfg = get_transport_cfg(h) + } + }; + + if (h->com == INVALID_HANDLE_VALUE) + { + fprintf(stderr, "%s: init_com failed\n", __func__); + goto failure; + } + else if (config_baud(h->com, cfg->baud)) + { + fprintf(stderr, "%s: config_com_baud 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) +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, + .com = init_com(cfg->dev), + .h = + { + .cfg = get_transport_cfg(h) + } + }; + + if (h->com == INVALID_HANDLE_VALUE) + { + fprintf(stderr, "%s: init_com failed\n", __func__); + goto failure; + } + else if (config_baud(h->com, cfg->baud)) + { + fprintf(stderr, "%s: config_com_baud failed\n", __func__); + goto failure; + } + + return h; + +failure: + net_close_serial(h); return NULL; } @@ -43,7 +413,19 @@ void net_deinit_serial(void) { } -const char *const *net_serial_devices(size_t *const n) +int net_set_event_serial(struct net_host_domain *const d, + const struct net_event *const ev) { - return NULL; + d->ev = *ev; + return 0; +} + +enum net_role net_role_serial(const struct net_host_domain *const h) +{ + return h->role; +} + +bool net_serial_unique(void) +{ + return false; } |
