449 lines
9.7 KiB
C
449 lines
9.7 KiB
C
#include <net.h>
|
|
#include <net_private.h>
|
|
#include <windows.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
struct net_host_domain
|
|
{
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
printf("%s: outgoing packet, buf: %p, n: %u: [", __func__, buf, (unsigned)n);
|
|
|
|
for (size_t i = 0; i < n; i++)
|
|
{
|
|
const char b = ((const char *)buf)[i];
|
|
printf("%02hhx", b);
|
|
|
|
if (isprint((unsigned char)b))
|
|
printf(" (%c)", b);
|
|
|
|
if (i + 1 < n)
|
|
printf(", ");
|
|
}
|
|
|
|
printf("]\n");
|
|
}
|
|
|
|
return get_result(h, r);
|
|
|
|
failure:
|
|
if (r)
|
|
free_req(h, r);
|
|
|
|
return -1;
|
|
}
|
|
|
|
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_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;
|
|
}
|
|
|
|
int net_init_serial(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void net_deinit_serial(void)
|
|
{
|
|
}
|
|
|
|
int net_set_event_serial(struct net_host_domain *const d,
|
|
const struct net_event *const ev)
|
|
{
|
|
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;
|
|
}
|