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/win9x | |
| parent | 39f50e601d395bbd2d78d0147ac530b756da2fff (diff) | |
| download | jancity-980858186149651df5543b6fc99a4f7db0cdd089.tar.gz | |
WIP
Diffstat (limited to 'src/net/win9x')
| -rw-r--r-- | src/net/win9x/src/serial.c | 404 |
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; } |
