aboutsummaryrefslogtreecommitdiff
path: root/src/net/win9x
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/win9x
parent39f50e601d395bbd2d78d0147ac530b756da2fff (diff)
downloadjancity-980858186149651df5543b6fc99a4f7db0cdd089.tar.gz
WIP
Diffstat (limited to 'src/net/win9x')
-rw-r--r--src/net/win9x/src/serial.c404
1 files changed, 393 insertions, 11 deletions
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;
}