262 lines
4.6 KiB
C
262 lines
4.6 KiB
C
#include <net.h>
|
|
#include <net/serial.h>
|
|
#include <net_serial_private.h>
|
|
#include <transport.h>
|
|
#include <psx.h>
|
|
#include <psxsio.h>
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
|
|
static struct net_host
|
|
{
|
|
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_close(struct net_host *const h)
|
|
{
|
|
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_update(struct net_host *const h)
|
|
{
|
|
return transport_update(&h->h);
|
|
}
|
|
|
|
static void on_received(const struct transport_event *const ev,
|
|
void *const arg)
|
|
{
|
|
struct net_host *const h = arg;
|
|
|
|
net_serial_on_received(ev, &h->ev);
|
|
}
|
|
|
|
static int on_write(const void *const buf, size_t n, void *const arg)
|
|
{
|
|
const size_t orig = n;
|
|
struct net_host *const h = arg;
|
|
|
|
EnterCriticalSection();
|
|
|
|
for (const uint8_t *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 (uint8_t *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
|
|
};
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
bool net_serial_unique(void)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
enum net_role net_role(const struct net_host *s)
|
|
{
|
|
return s->role;
|
|
}
|