/*
generic/ip_ps.c

pseudo IP specific part of the IP implementation

Created:	Apr 23, 1993 by Philip Homburg

Copyright 1995 Philip Homburg
*/

#include "inet.h"
#include "assert.h"
#include "type.h"
#include "buf.h"
#include "event.h"
#include "ip.h"
#include "ip_int.h"
#include "psip.h"

THIS_FILE

FORWARD void ipps_main ARGS(( ip_port_t *ip_port ));
FORWARD void ipps_set_ipaddr ARGS(( ip_port_t *ip_port ));
FORWARD int ipps_send ARGS(( struct ip_port *ip_port, ipaddr_t dest, 
					acc_t *pack, int type ));

PUBLIC int ipps_init(ip_port)
ip_port_t *ip_port;
{
	int result;

	result= psip_enable(ip_port->ip_dl.dl_ps.ps_port, ip_port->ip_port);
	if (result == -1)
		return -1;
	ip_port->ip_dl.dl_ps.ps_send_head= NULL;
	ip_port->ip_dl.dl_ps.ps_send_tail= NULL;
	ip_port->ip_dev_main= ipps_main;
	ip_port->ip_dev_set_ipaddr= ipps_set_ipaddr;
	ip_port->ip_dev_send= ipps_send;
	return result;
}

PUBLIC void ipps_get(ip_port_nr)
int ip_port_nr;
{
	int result;
	ipaddr_t dest;
	acc_t *acc, *pack, *next_part;
	ip_port_t *ip_port;

	assert(ip_port_nr >= 0 && ip_port_nr < ip_conf_nr);
	ip_port= &ip_port_table[ip_port_nr];
	assert(ip_port->ip_dl_type == IPDL_PSIP);

	while (ip_port->ip_dl.dl_ps.ps_send_head != NULL)
	{
		pack= ip_port->ip_dl.dl_ps.ps_send_head;
		ip_port->ip_dl.dl_ps.ps_send_head= pack->acc_ext_link;

		/* Extract nexthop address */
		pack= bf_packIffLess(pack, sizeof(dest));
		dest= *(ipaddr_t *)ptr2acc_data(pack);
		pack= bf_delhead(pack, sizeof(dest));

		if (bf_bufsize(pack) > ip_port->ip_mtu)
		{
			next_part= pack;
			pack= ip_split_pack(ip_port, &next_part, 
				ip_port->ip_mtu);
			if (pack == NULL)
				continue;

			/* Prepend nexthop address */
			acc= bf_memreq(sizeof(dest));
			*(ipaddr_t *)(ptr2acc_data(acc))= dest;
			acc->acc_next= next_part;
			next_part= acc; acc= NULL;

			assert(next_part->acc_linkC == 1);
			next_part->acc_ext_link= NULL;
			if (ip_port->ip_dl.dl_ps.ps_send_head)
			{
				ip_port->ip_dl.dl_ps.ps_send_tail->
					acc_ext_link= next_part;
			}
			else
			{
				ip_port->ip_dl.dl_ps.ps_send_head=
					next_part;
			}
			ip_port->ip_dl.dl_ps.ps_send_tail= next_part;
		}

		result= psip_send(ip_port->ip_dl.dl_ps.ps_port, dest, pack);
		if (result != NW_SUSPEND)
		{
			assert(result == NW_OK);
			continue;
		}

		/* Prepend nexthop address */
		acc= bf_memreq(sizeof(dest));
		*(ipaddr_t *)(ptr2acc_data(acc))= dest;
		acc->acc_next= pack;
		pack= acc; acc= NULL;

		pack->acc_ext_link= ip_port->ip_dl.dl_ps.ps_send_head;
		ip_port->ip_dl.dl_ps.ps_send_head= pack;
		if (pack->acc_ext_link == NULL)
			ip_port->ip_dl.dl_ps.ps_send_tail= pack;
		break;
	}
}

PUBLIC void ipps_put(ip_port_nr, nexthop, pack)
int ip_port_nr;
ipaddr_t nexthop;
acc_t *pack;
{
	ip_port_t *ip_port;

	assert(ip_port_nr >= 0 && ip_port_nr < ip_conf_nr);
	ip_port= &ip_port_table[ip_port_nr];
	assert(ip_port->ip_dl_type == IPDL_PSIP);
	if (nexthop == HTONL(0xffffffff))
		ip_arrived_broadcast(ip_port, pack);
	else
		ip_arrived(ip_port, pack);
}

PRIVATE void ipps_main(ip_port)
ip_port_t *ip_port;
{
	/* nothing to do */
}

PRIVATE void ipps_set_ipaddr(ip_port)
ip_port_t *ip_port;
{
}

PRIVATE int ipps_send(ip_port, dest, pack, type)
struct ip_port *ip_port;
ipaddr_t dest;
acc_t *pack;
int type;
{
	int result;
	acc_t *acc, *next_part;

	if (type != IP_LT_NORMAL)
	{
		ip_arrived_broadcast(ip_port, bf_dupacc(pack));

		/* Map all broadcasts to the on-link broadcast address.
		 * This saves the application from having to to find out
		 * if the destination is a subnet broadcast.
		 */
		dest= HTONL(0xffffffff);
	}

	/* Note that allocating a packet may trigger a cleanup action,
	 * which may cause the send queue to become empty.
	 */
	while (ip_port->ip_dl.dl_ps.ps_send_head != NULL)
	{
		acc= bf_memreq(sizeof(dest));

		if (ip_port->ip_dl.dl_ps.ps_send_head == NULL)
		{
			bf_afree(acc); acc= NULL;
			continue;
		}

		/* Prepend nexthop address */
		*(ipaddr_t *)(ptr2acc_data(acc))= dest;
		acc->acc_next= pack;
		pack= acc; acc= NULL;

		assert(pack->acc_linkC == 1);
		pack->acc_ext_link= NULL;

		ip_port->ip_dl.dl_ps.ps_send_tail->acc_ext_link= pack;
		ip_port->ip_dl.dl_ps.ps_send_tail= pack;

		return NW_OK;
	}

	while (pack)
	{
		if (bf_bufsize(pack) > ip_port->ip_mtu)
		{
			next_part= pack;
			pack= ip_split_pack(ip_port, &next_part, 
				ip_port->ip_mtu);
			if (pack == NULL)
			{
				return NW_OK;
			}

			/* Prepend nexthop address */
			acc= bf_memreq(sizeof(dest));
			*(ipaddr_t *)(ptr2acc_data(acc))= dest;
			acc->acc_next= next_part;
			next_part= acc; acc= NULL;

			assert(next_part->acc_linkC == 1);
			next_part->acc_ext_link= NULL;
			ip_port->ip_dl.dl_ps.ps_send_head= next_part;
			ip_port->ip_dl.dl_ps.ps_send_tail= next_part;
		}
		result= psip_send(ip_port->ip_dl.dl_ps.ps_port, dest, pack);
		if (result == NW_SUSPEND)
		{
			/* Prepend nexthop address */
			acc= bf_memreq(sizeof(dest));
			*(ipaddr_t *)(ptr2acc_data(acc))= dest;
			acc->acc_next= pack;
			pack= acc; acc= NULL;

			assert(pack->acc_linkC == 1);
			pack->acc_ext_link= ip_port->ip_dl.dl_ps.ps_send_head;
			ip_port->ip_dl.dl_ps.ps_send_head= pack;
			if (!pack->acc_ext_link)
				ip_port->ip_dl.dl_ps.ps_send_tail= pack;
			break;
		}
		assert(result == NW_OK);
		pack= ip_port->ip_dl.dl_ps.ps_send_head;
		if (!pack)
			break;
		ip_port->ip_dl.dl_ps.ps_send_head= pack->acc_ext_link;

		/* Extract nexthop address */
		pack= bf_packIffLess(pack, sizeof(dest));
		dest= *(ipaddr_t *)ptr2acc_data(pack);
		pack= bf_delhead(pack, sizeof(dest));
	}

	return NW_OK;
}

#if 0
int ipps_check(ip_port_t *ip_port)
{
	int n, bad;
	acc_t *prev, *curr;

	for (n= 0, prev= NULL, curr= ip_port->ip_dl.dl_ps.ps_send_head_;
		curr; prev= curr, curr= curr->acc_ext_link)
	{
		n++;
	}
	bad= 0;
	if (prev != NULL && prev != ip_port->ip_dl.dl_ps.ps_send_tail_)
	{
		printf("ipps_check, ip[%d]: wrong tail: got %p, expected %p\n",
			ip_port-ip_port_table,
			ip_port->ip_dl.dl_ps.ps_send_tail_, prev);
		bad++;
	}
	if (n != ip_port->ip_dl.dl_ps.ps_send_nr)
	{
		printf("ipps_check, ip[%d]: wrong count: got %d, expected %d\n",
			ip_port-ip_port_table,
			ip_port->ip_dl.dl_ps.ps_send_nr, n);
		bad++;
	}
	return bad == 0;
}
#endif

/*
 * $PchId: ip_ps.c,v 1.15 2003/01/21 15:57:52 philip Exp $
 */
