/*
 * Copyright (c) University of British Columbia, 1984
 * Copyright (c) 1990 The Regents of the University of California.
 * All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * the Laboratory for Computation Vision and the Computer Science Department
 * of the University of British Columbia.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)hd_timer.c	7.4 (Berkeley) 5/29/91
 */

#include <linux/config.h>
#ifdef CONFIG_AX25
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/string.h>
#include <linux/sockios.h>
#include <linux/net.h>
#include "ax25.h"
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include "sock.h"
#include <asm/segment.h>
#include <asm/system.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/interrupt.h>

/*
 *	Linux set/reset timer routines
 */

void ax25_set_timer(ax25_socket *sk)
{
	unsigned long flags;
	
	save_flags(flags);
	cli();
	sk->timeout=0;
	del_timer(&sk->timer);
	restore_flags(flags);
	
	sk->timer.data=(unsigned long)sk;
	sk->timer.function=&ax25_timer;
	/* t1 increases with the number of digipeaters */
	if(sk->opt.ax25.digipeat)
		sk->opt.ax25.timer=sk->opt.ax25.t1 * (sk->opt.ax25.digipeat->ndigi);
	else
		sk->opt.ax25.timer=sk->opt.ax25.t1;
	sk->timeout=0;
	sk->timer.expires=10;
	add_timer(&sk->timer);
}

void ax25_reset_timer(ax25_socket *sk)
{
	unsigned long flags;
	
	save_flags(flags);
	cli();
	sk->timeout=0;
	del_timer(&sk->timer);
	restore_flags(flags);

	sk->timer.data=(unsigned long)sk;
	sk->timer.function=&ax25_timer;
	sk->timeout=0;
	sk->timer.expires=10;
	add_timer(&sk->timer);
}

/*
 *  AX.25 TIMER 
 *
 *  This routine is called every 500ms. Decrement timer by this
 *  amount - if expired then process the event.
 */

void ax25_timer(unsigned long param)
{
	ax25_socket *sk=(ax25_socket *)param;

	if (sk->opt.ax25.rrtimer && (--sk->opt.ax25.rrtimer == 0)) 
	{
		if (sk->opt.ax25.lasttxnr != sk->opt.ax25.vr)
			ax25_write_internal (sk, RR, POLLOFF, C_RESPONSE);
	}
	if (!(sk->opt.ax25.timer && --sk->opt.ax25.timer == 0))
	{
		ax25_reset_timer(sk);
		return;
	}

	if(sk->debug)
		printk("Timer event (state = %d)\n",sk->opt.ax25.state);
		
	switch (sk->opt.ax25.state) 
	{
		case INIT: 	/* Not init any more */
		case DISC_SENT:
			if (++sk->opt.ax25.retxcnt == (signed int)sk->opt.ax25.n2) 
			{
				sk->opt.ax25.retxcnt = 0;
				ax25_write_internal (sk, DM, POLLOFF, C_COMMAND);
				sk->opt.ax25.state = DM_SENT;
				sk->state=TCP_CLOSE;
				sk->err=ETIMEDOUT;
				if(!sk->dead)
					sk->state_change(sk);
			}
			else
			{
				if(sk->debug)
					printk("Sending DISC(P)\n");
				ax25_write_internal (sk, DISC, POLLON, C_COMMAND);
			}
			break;

		case ABM: 
			if (sk->opt.ax25.lastrxnr != sk->opt.ax25.vs) 
			{	/* XXX */
				ax25_resend_iframe(sk);
			}
			break;

		case WAIT_SABM: 
			break;

		case DM_SENT: 
			sk->opt.ax25.retxcnt = 0;
			sk->opt.ax25.state = DISCONNECTED;
			break;

		case WAIT_UA: 
			if (++sk->opt.ax25.retxcnt == (signed int)sk->opt.ax25.n2) 
			{
				sk->opt.ax25.retxcnt = 0;
				ax25_write_internal (sk, DM, POLLOFF, C_COMMAND);
				sk->opt.ax25.state = DM_SENT;
				sk->state=TCP_CLOSE;
				sk->err=ETIMEDOUT;
				if(!sk->dead)
					sk->state_change(sk);
			} else
				ax25_write_internal (sk, SABM, POLLON, C_COMMAND);
			break;

		case SABM_SENT: 
			printk("Oops this state has been obsoleted!\n");
			break;

		case DISCONNECTED:
			/* Magic here: If we listen() and a new link dies before it
			   is accepted() it isnt 'dead' so doesnt get removed. */
			if(sk->dead)
			{
				KILL_TIMER(sk);
				ax25_destroy_socket(sk);
				return;
			}
			break;
	}
	SET_TIMER (sk);
}



#endif
