/*
 * Micro-X -- an X server for DOS
 * Copyright (C) 1993 StarNet Communications Corp.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 * 
 * StarNet Communications Corp.
 * 550 Lakeside Dr. #3
 * Sunnyvale CA 94086 US
 * http://www.starnet.com
 * x-dos@starnet.com
 */

#include "pcdefs.h"
#include "protocol.h"
#include "tcp.h"
#include "funcdef.h"

extern struct port _far *ports[NPORTS];

/************************************************************************/
/*  tcptick
*      sleep, while demuxing packets, so we don't miss anything
*
*/
/* void */
void
tcptick()
{
	int	i;
	u_long	time;
	struct tcpport _far *p;

/*
*  Check each port to see if retransmission is necessary.
*/
	for (i = 0; i < NPORTS; i++) {
		p = (struct tcpport _far *)ports[i];

		if (!p || !(p->flag & O_STREAM))
			continue;

		if (p->state == SCLOSED || p->state == SLISTEN)
			continue;

		time = n_clicks();
		if (p->lasttime > time)		/* midnight wrap */
			p->lasttime = 1L;

		if (p->state == STWAIT) {
			if (p->lasttime + WAITTIME < time) {
			    p->state = SCLOSED;
			    if (p->flag & O_RELEASED)
				p->flag = 0;
			}
			continue;
		}
		if (p->state >= SSYNRS && p->state <= SSYNS) {
			/* SSYNRS, SSYNR, and SSYNS */
			if (p->lasttime + 9L < time)
				tcpsend(p,
				    (p->outpkt.t.hlen >> 2) & 0x3c -
				    sizeof (struct tcph));
			continue;
		}
		if (p->wait_tx) {
			p->wait_tx--;
			if (!p->wait_tx) {
				transq(p);
				continue;
			}
		}
		if (p->outfree || p->state > SEST) {
/*
*  if a retransmission timeout occurs, exponential back-off.
*  This number returns toward the correct value by the RTT measurement
*  code in ackcheck.
*
*  fix: 5/12/88, if timer was at MAXRTO, transq didn't get hit - TK
*/
			if (p->rto < (u_int)(time - p->lasttime)) {
			    if (p->rto < MAXRTO) 
				p->rto = p->rto << 1;
			    if (p->resends > MAXRESENDS) {
				p->state = SCLOSED;
				if (p->flag & O_RELEASED)
				    p->flag = 0;
			    } else {
				p->resends++;
				transq(p);
			    }
			}
		}
	}
}

/************************************************************************/
/*  transq
*
*   Needed for TCP, not as general as cpqueue, 
*   but is required for efficient transmit of the whole window.
*
*   Transmit the entire queue (window) to the other host without expecting
*   any sort of acknowledgement.
*
*/
void
transq(p)
struct tcpport _far *p;
{
	u_int bites;

/*
*   find out how many bytes the other side will allow us to send (window)
*/
	bites = min(min(p->outsize, p->outfree), p->outwin);
	if (bites == 0 && p->outfree)
		bites = 1;

	if (bites) {
		memcpy(p->outpkt.x.data, p->dataout, bites);
	}
	p->wait_tx = 0;
	tcpsend(p, bites);		/* send it */
}
