aboutsummaryrefslogtreecommitdiff
path: root/src/net
diff options
context:
space:
mode:
authorXavier Del Campo Romero <xavi.dcr@tutanota.com>2022-09-27 17:03:06 +0200
committerXavier Del Campo Romero <xavi.dcr@tutanota.com>2022-11-01 16:26:16 +0100
commit980858186149651df5543b6fc99a4f7db0cdd089 (patch)
treed347200b0a562d84df505097651ad0642f207fdd /src/net
parent39f50e601d395bbd2d78d0147ac530b756da2fff (diff)
downloadjancity-980858186149651df5543b6fc99a4f7db0cdd089.tar.gz
WIP
Diffstat (limited to 'src/net')
-rw-r--r--src/net/CMakeLists.txt5
-rw-r--r--src/net/inc/net.h65
-rw-r--r--src/net/inc/net/serial.h4
-rw-r--r--src/net/posix/src/serial.c276
-rw-r--r--src/net/privinc/net_private.h34
-rw-r--r--src/net/privinc/net_serial_private.h19
-rw-r--r--src/net/ps1/src/net.c238
-rw-r--r--src/net/src/enet/ipv4.c111
-rw-r--r--src/net/src/net.c99
-rw-r--r--src/net/src/serial.c37
-rw-r--r--src/net/win9x/src/serial.c404
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;
}