#include "funcdef.h"
#include "local.h"
#include "../defs.h"
#include "../funcs.h"

u_long broadcast_addr;

int _near errno;

char _near shared_buf[4096];

struct bw_func {
	int (_fastcall *proc)(short _far *);
} _near bw_funcs[] = {
	0,
	bw_init,
	bw_shutdown,
	bw_socket,
	bw_info,
	bw_accept,
	bw_drain,
	bw_release,
	bw_eof,
	bw_read,
	bw_write,
	bw_recv,
	bw_sendto,
	bw_select,
	0
};

#define n_funcs (sizeof (bw_funcs) / sizeof (bw_funcs[0]))

int
rpc_entry(buf, size)
short _far *buf;
int size;
{
    struct bw_func near *nfp;
    int i;

    nfp = &bw_funcs[buf[0]];
    if ((unsigned)buf[0] >= n_funcs || !nfp->proc) {
	buf[0] = -1;
	buf[2] = NETERR_BADCALL;
	return 6;
    }
    i = (nfp->proc)(buf);
    if (i < 0) {
	buf[0] = i;
	buf[1] = errno;
	buf[2] = convert_error(errno);
	return 6;
    }
    return i;
}

struct eth _eth;

int _fastcall
bw_init(buf)
short _far *buf;
{
    long netmask;
    int fd;

    dfputs("Beame & Whiteside TCP/IP transport layer\r\n");
    fd = open("ETHDEV27", 0x42);
    if (fd < 0) {
	dfputs("BWTCP is not installed\r\n");
	return -1;
    }

    /* set binary mode */
    (void)ioctl(fd, 1, (char *)0x60, 0);

    /* Read configuration information from device. */
    (void)read(fd, (char *)&_eth, sizeof _eth);

    (void)close(fd);

    /* Issue interrupt to device with function = 21.  I don't know
     * why we do this but it was in the example from B&W.
     */
    _asm	mov	al,_eth.vector
    _asm	xor	ah,ah
    _asm	push	ax
    _asm	mov	ah,21
    _asm	call	int86

    /* compute netmask */
    netmask = 0x80000000;
    netmask >>= _eth.subnet_mask - 1;
    netmask = htonl(netmask);

    broadcast_addr = (_eth.internet_address & netmask) | (-1 & ~netmask);

    buf[0] = 0;
    buf[2] = (u_short)(u_long)(char _far *)shared_buf;
    buf[3] = (u_short)((u_long)(char _far *)shared_buf >> 16);
    buf[4] = sizeof shared_buf;
    return 10;
}

int _fastcall
bw_shutdown(buf)
short _far *buf;
{
    return 0;
}

int _fastcall
bw_info(buf)
short _far *buf;
{
    struct net_info _far *ni = (struct net_info _far *)&buf[1];

    ni->ip_address = _eth.internet_address;
    ni->ip_broadcast = broadcast_addr;
    buf[0] = 0;
    return sizeof (struct net_info) + sizeof (short);
}

int _fastcall
bw_socket(buf)
short _far *buf;
{
    int type = buf[1];
    struct net_addr _far *addr = (struct net_addr _far *)&buf[2];
    int fd;
    char *name;
    u_char ioctl_buf[3];

    switch (type) {
	case NET_TYPE_TCP:
	    name = "TCP-IP10";
	    break;
	case NET_TYPE_UDP:
	    name = "UDP-IP10";
	    break;
	default:
	    dfputs("unknown socket type\r\n");
	    return -1;
    }
    fd = open(name, 0x42);
    if (fd < 0)
	return -1;
    /* set binary mode */
    (void)ioctl(fd, 1, (char *)0x60, 0);

    /* bind the socket */
    if (bind(fd, addr->socket, type) < 0) {
	close(fd);
	return -1;
    }
    ioctl_buf[0] = 6;		/* set flags */
    ioctl_buf[1] = 0x81;	/* 0x80 = buffer reclaim (?) 1 = nonblocking */
    ioctl(fd, 3, ioctl_buf, 2);
    buf[0] = fd;
    return 2;
}

int _fastcall
bind(fd, port, type)
int fd;
u_short port;
int type;
{
    u_char ioctl_buf[3];

    if (port == 0) {
	/* get a port */
	_asm	mov	al,_eth.vector
	_asm	xor	ah,ah
	_asm	push	ax
	_asm	mov	ah,36
	_asm	call	int86
	_asm	mov	port,ax
    }
    ioctl_buf[0] = 0;		/* bind */
    *(short *)&ioctl_buf[1] = port;
    ioctl(fd, 3, ioctl_buf, 3);
    if (ioctl_buf[0]) {
	errno = ioctl_buf[0];
	return -1;
    }
    if (type == NET_TYPE_TCP) {
	ioctl_buf[0] = 2;		/* put in listen mode */
	ioctl(fd, 3, ioctl_buf, 1);
	if (ioctl_buf[0]) {
	    errno = ioctl_buf[0];
	    return -1;
	}
    }
    return 0;
}
