/* File MSNPKT.C
 * Packet Driver interface
 *
 * Copyright (C) 1985, 1993, Trustees of Columbia University in the 
 * City of New York.  Permission is granted to any individual or institution
 * to use this software as long as it is not sold for profit.  This copyright
 * notice must be retained.  This software may not be included in commercial
 * products without written permission of Columbia University.
 *
 * Written for MS-DOS Kermit by Joe R. Doupnik, 
 *  Utah State University, jrd@cc.usu.edu, jrd@usu.Bitnet
 *
 * Last edit
 * 27 August 1992 v3.13
 */
#include "msntcp.h"
#include "msnlib.h"

#define BUFSIZE	6000 			/* size of pkt receive buffer */

word	pkt_ip_type = 0x0008;		/* these are little endian values */
word	pkt_arp_type = 0x0608;
word	pkt_rarp_type = 0x3580;

int	pkt_ip_handle = -1;		/* -1 means invalid handle */
int	pkt_arp_handle = -1;
int	pkt_rarp_handle = -1;

int	pdversion, pdtype, pdnum, pdfunct;
extern	word pktdevclass;
extern	int pdinit(void *, void *);
extern	int pdinfo(int *, int *, int *, int *, int *);
extern	int pdaccess(int *, int, int *);
extern	int pdclose(int);
extern	eth_address eth_addr;			/* six byte array */
extern	word MAC_len;
extern	word mss;

byte pktbuf[BUFSIZE] = {0};	/* linked list packet receive buffer */
static byte * pktbuf_read = pktbuf;
byte pktwnum = 0;		/* seq number for packets written to buffer */
byte pktrnum = 0;		/* seq number for packets read from buffer */
byte * pktbuf_wrote = &pktbuf[BUFSIZE - 4];

int
pkt_eth_init()
{
    	pkt_buf_wipe();		/* clean out and init receiver buffer */
	mss = ETH_MSS;		/* set default max seg size */
	if (pdinit(pktbuf, eth_addr) == 0)
    		{
		outs("\r\nCannot attach to an Ethernet Packet Driver");
		outs(" or a Novell ODI driver.");
		return( 0 );
		}
	pkt_buf_wipe();
    				/* lets find out about the driver */
	if ((pdinfo(&pdversion, &pktdevclass, &pdtype, &pdnum, &pdfunct))
		== 0)
		{
		outs("\r\nCannot obtain Packet Driver or ODI information");
		return (0);
		}

	if (pdaccess(&pkt_ip_type, 
	(pktdevclass == PD_SLIP)? 0: sizeof(pkt_ip_handle), &pkt_ip_handle)
		== 0)
 		{
		outs("\r\n\7Cannot access IP type packets");
		return (0);
		}

	if ((pdinfo(&pdversion, &pktdevclass, &pdtype, &pdnum, &pdfunct))
		== 0)
		{
		outs("\r\nCannot obtain Packet Driver or ODI information");
		return (0);
		}

/* Check for real SLIP and for ODI with SLIP_PPP; neither uses ARP and RARP */
/* ODI returns length of MAC header, such as 6 for real Ethernet and 0
   for SLIP and PPP. We get Ethernet style frames for ODI material. */
	if (pktdevclass == PD_SLIP ||
		( pktdevclass == PD_ETHER && MAC_len == 0))
		return (1);

	if (pdaccess(&pkt_arp_type, sizeof(pkt_arp_handle), &pkt_arp_handle)
		== 0)
 		{
		outs("\r\n\7Cannot access ARP type packets");
		return (0);
		}
	return (1);				/* say success */
}

int
pkt_rarp_init()				/* access PD or ODI for RARP */
{
	if (pkt_rarp_handle != -1)
		return (1);		/* have handle already */

	if (pdaccess(&pkt_rarp_type, sizeof(pkt_rarp_handle), &pkt_rarp_handle)
		== 0)
 		{
		outs("\r\n\7Cannot access RARP type packets");
		return (0);
		}
	return (1);				/* say success */
}

int
pkt_release()
{
	register int status = 1;			/* assume success */

	if (pkt_ip_handle != -1)
		if (pdclose(pkt_ip_handle) == 0)
			{
			outs("\r\nERROR releasing Packet Driver for IP");
			status = 0;
			}
		else pkt_ip_handle = -1;	/* handle is out of service */

    	if (pkt_arp_handle != -1)
		if (pdclose(pkt_arp_handle) == 0)
			{
			outs("\r\nERROR releasing Packet Driver for ARP");
			status = 0;
			}
		else pkt_arp_handle = -1;	/* handle is out of service */

	if (pkt_rarp_handle != -1)
		if (pdclose(pkt_rarp_handle) == 0)
			{
			outs("\r\nERROR releasing Packet Driver for RARP");
			status = 0;
			}
		else pkt_rarp_handle = -1;	/* handle is out of service */
	return (status);
}

/* Deliver pointer to start of packet (destination address) or NULL.
 * Simple linked list: byte flag, byte pkt seq number, int count, 
 * byte data[count] and so on.
 * Status on the link flag byte is
 * 0 = end of buffer (count has size to point to start of buffer)
 * 1 = this slot is free (unused)
 * 2 = this slot has an unread packet
 * 4 = this slot is allocated for a packet, packet not yet loaded into it
 * 8 = this slot has a packet which has been read once
 * A two byte count value follows the flag byte, for number of bytes in 
 * this slot. Pktbuf_read remembers the pointer to the last-read slot.
*/

void * 
pkt_received()
{
	register byte * p;
	register int i;

	p = pktbuf_read;			/* start with last read */
	for (i = 0; i < 2 * (BUFSIZE / (60 + 4)); i++)	/* 2 * max pkts */
		{
		if (*p == 2 && *(p+1) == pktrnum) /* 2 == ready to be read */
			{			/* if this is the next pkt */
 			pktbuf_read = p;	/* where we have read */
			*p = 8;			/* mark as have read */
			pktrnum++;		/* next one to read */
			return (p + 4);		/* return ptr to pkt*/
			}
/* if link is:      end of buf  free      ready      allocated  have read */
		if (*p == 0 || *p == 1 || *p == 2 || *p == 4 || *p == 8)
			{
			p += 4 + *(word *)(p+2);   /* point at next link */
			continue;
			}
		else				/* bad link information */
			{
			pkt_buf_wipe();		/* emergency treatment */
			break;
			}
		if (p == pktbuf_read) break;	/* where we came in */
		}
	return (NULL);
}
	
void
pkt_buf_release(byte *p)	/* return a buffer to the pool */
{
	disable();		/* disallow interrupts here */
	if (pktdevclass == PD_SLIP)
		p -= 4;			/* just link info */
	else
		p -= 4 + 6 + 6 + 2;	/* link info and MAC header */

	if (*p == 8)		/* if packet has been read */
		*p = 1;		/* mark link as freed */
	enable();
}

void
pkt_buf_wipe()					/* clear all buffers */
{
	disable();
	pktbuf[0] = 1;				/* flag first link as free */
	pktbuf[1] = 0;
	*(word *)&pktbuf[2] = BUFSIZE - 8; /* free space, size - two links */

	pktbuf[BUFSIZE - 4] = 0;		/* flag as end of buffer */
	pktbuf[BUFSIZE - 3] = 0;		/* pkt buffer seq number */
				/* count below points to start of buffer */
	*(int *)&pktbuf[BUFSIZE - 2] = - BUFSIZE; 
	pktbuf_read = pktbuf;				/* where last read */
	pktbuf_wrote = &pktbuf[BUFSIZE - 4];		/* where last wrote*/
	pktwnum = pktrnum = 0;		/* reset buffer sequence numbers */
	enable();
}

