xref: /csrg-svn/sys/netiso/tp.trans (revision 39921)
136396Ssklower/***********************************************************
236396Ssklower		Copyright IBM Corporation 1987
336396Ssklower
436396Ssklower                      All Rights Reserved
536396Ssklower
636396SsklowerPermission to use, copy, modify, and distribute this software and its
736396Ssklowerdocumentation for any purpose and without fee is hereby granted,
836396Ssklowerprovided that the above copyright notice appear in all copies and that
936396Ssklowerboth that copyright notice and this permission notice appear in
1036396Ssklowersupporting documentation, and that the name of IBM not be
1136396Ssklowerused in advertising or publicity pertaining to distribution of the
1236396Ssklowersoftware without specific, written prior permission.
1336396Ssklower
1436396SsklowerIBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
1536396SsklowerALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
1636396SsklowerIBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
1736396SsklowerANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
1836396SsklowerWHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
1936396SsklowerARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
2036396SsklowerSOFTWARE.
2136396Ssklower
2236396Ssklower******************************************************************/
2336396Ssklower
2436396Ssklower/*
2536396Ssklower * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
2636396Ssklower */
2736396Ssklower/* $Header: tp.trans,v 5.1 88/10/12 12:22:07 root Exp $
2836396Ssklower *
2936396Ssklower * Transition file for TP.
3036396Ssklower *
3136396Ssklower * DO NOT:
3236396Ssklower * - change the order of any of the events or states.  to do so will
3336396Ssklower *   make tppt, netstat, etc. cease working.
3436396Ssklower *
3536396Ssklower * NOTE:
3636396Ssklower * some hooks exist for data on (dis)connect, but it's ***NOT***SUPPORTED***
3736396Ssklower * (read: may not work!)
3836396Ssklower *
3936396Ssklower * I tried to put everything that causes a change of state in here, hence
4036396Ssklower * there are some seemingly trivial events  like T_DETACH and T_LISTEN_req.
4136396Ssklower *
4236396Ssklower * Almost everything having to do w/ setting & cancelling timers is here
4336396Ssklower * but once it was debugged, I moved the setting of the
4436396Ssklower * keepalive (sendack) timer to tp_emit(), where an AK_TPDU is sent.
4536396Ssklower * This is so the code wouldn't be duplicated all over creation in here.
4636396Ssklower *
4736396Ssklower */
4836396Ssklower*PROTOCOL tp
4936396Ssklower
5036396Ssklower*INCLUDE
5136396Ssklower
5236396Ssklower{
53*39921Ssklower/*	@(#)tp.trans	7.4 (Berkeley) 01/16/90 */
5436396Ssklower#include "param.h"
5536396Ssklower#include "socket.h"
5636396Ssklower#include "socketvar.h"
5736396Ssklower#include "protosw.h"
5836396Ssklower#include "mbuf.h"
5936396Ssklower#include "time.h"
6036396Ssklower#include "errno.h"
6136396Ssklower#include "../netiso/tp_param.h"
6236396Ssklower#include "../netiso/tp_stat.h"
6336396Ssklower#include "../netiso/tp_pcb.h"
6436396Ssklower#include "../netiso/tp_tpdu.h"
6536396Ssklower#include "../netiso/argo_debug.h"
6636396Ssklower#include "../netiso/tp_trace.h"
6736396Ssklower#include "../netiso/iso_errno.h"
6836396Ssklower#include "../netiso/tp_seq.h"
6936396Ssklower#include "../netiso/cons.h"
7036396Ssklower
7136396Ssklower#define DRIVERTRACE TPPTdriver
7237469Ssklower#define sbwakeup(sb)	sowakeup(p->tp_sock, sb);
7337469Ssklower#define MCPY(d, w) (d ? m_copym(d, 0, (int)M_COPYALL, w): 0)
7436396Ssklower
7536396Ssklowerstatic 	trick_hc = 1;
7636396Ssklower
7736396Ssklowerint 	tp_emit(),
7836396Ssklower		tp_goodack(),				tp_goodXack(),
7936396Ssklower		tp_stash()
8036396Ssklower;
8136396Ssklowervoid	tp_indicate(),				tp_getoptions(),
8236396Ssklower		tp_soisdisconnecting(), 	tp_soisdisconnected(),
8336396Ssklower		tp_recycle_tsuffix(),
8436396Ssklower		tp_etimeout(),				tp_euntimeout(),
8536396Ssklower		tp_euntimeout_lss(),		tp_ctimeout(),
8636396Ssklower		tp_cuntimeout(),			tp_ctimeout_MIN(),
8736396Ssklower		tp_freeref(),				tp_detach(),
8836396Ssklower		tp0_stash(), 				tp0_send(),
8936396Ssklower		tp_netcmd(),				tp_send()
9036396Ssklower;
9136396Ssklower
9236396Ssklowertypedef  struct tp_pcb tpcb_struct;
9336396Ssklower
9436396Ssklower
9536396Ssklower}
9636396Ssklower
9736396Ssklower*PCB    tpcb_struct 	SYNONYM  P
9836396Ssklower
9936396Ssklower*STATES
10036396Ssklower
10136396SsklowerTP_CLOSED
10236396SsklowerTP_CRSENT
10336396SsklowerTP_AKWAIT
10436396SsklowerTP_OPEN
10536396SsklowerTP_CLOSING
10636396SsklowerTP_REFWAIT
10736396SsklowerTP_LISTENING	/* Local to this implementation */
10838841SsklowerTP_CONFIRMING	/* Local to this implementation */
10936396Ssklower
11036396Ssklower*EVENTS		{ struct timeval e_time; } 		SYNONYM  E
11136396Ssklower
11236396Ssklower /*
11336396Ssklower  * C (typically cancelled) timers  -
11436396Ssklower  *
11536396Ssklower  * let these be the first ones so for the sake of convenience
11636396Ssklower  * their values are 0--> n-1
11736396Ssklower  * DO NOT CHANGE THE ORDER OF THESE TIMER EVENTS!!
11836396Ssklower  */
11936396Ssklower TM_inact
12036396Ssklower TM_retrans
12136396Ssklower				/* TM_retrans is used for all
12236396Ssklower				 * simple retransmissions - CR,CC,XPD,DR
12336396Ssklower				 */
12436396Ssklower
12536396Ssklower TM_sendack
12636396Ssklower				/* TM_sendack does dual duty - keepalive AND sendack.
12736396Ssklower				 * It's set w/ keepalive-ticks every time an ack is sent.
12836396Ssklower				 * (this is done in (void) tp_emit() ).
12936396Ssklower				 * It's cancelled and reset whenever a DT
13036396Ssklower				 * arrives and it doesn't require immediate acking.
13136396Ssklower				 * Note that in this case it's set w/ the minimum of
13236396Ssklower				 * its prev value and the sendack-ticks value so the
13336396Ssklower				 * purpose of the keepalive is preserved.
13436396Ssklower				 */
13536396Ssklower TM_notused
13636396Ssklower
13736396Ssklower /*
13836396Ssklower  * E (typically expired) timers - these may be in any order.
13936396Ssklower  * These cause procedures to be executed directly; may not
14036396Ssklower  * cause an 'event' as we know them here.
14136396Ssklower  */
14236396Ssklower TM_reference		{ SeqNum e_low; SeqNum e_high; int e_retrans; }
14336396Ssklower TM_data_retrans	{ SeqNum e_low; SeqNum e_high; int e_retrans; }
14436396Ssklower
14536396Ssklower/* NOTE: in tp_input is a minor optimization that assumes that
14636396Ssklower * for all tpdu types that can take e_data and e_datalen, these
14736396Ssklower * fields fall in the same place in the event structure, that is,
14836396Ssklower * e_data is the first field and e_datalen is the 2nd field.
14936396Ssklower */
15036396Ssklower
15136396Ssklower ER_TPDU  	 	{
15236396Ssklower				  u_char		e_reason;
15336396Ssklower				}
15436396Ssklower CR_TPDU  	 	{ struct mbuf 	*e_data;	/* first field */
15536396Ssklower				  int 			e_datalen; /* 2nd field */
15636396Ssklower				  u_int			e_cdt;
15736396Ssklower				}
15836396Ssklower DR_TPDU   	 	{ struct mbuf 	*e_data;	/* first field */
15936396Ssklower				  int 			e_datalen; /* 2nd field */
16036396Ssklower				  u_short		e_sref;
16136396Ssklower				  u_char		e_reason;
16236396Ssklower				}
16336396Ssklower DC_TPDU
16436396Ssklower CC_TPDU   	 	{ struct mbuf 	*e_data;	/* first field */
16536396Ssklower				  int 			e_datalen; /* 2nd field */
16636396Ssklower				  u_short		e_sref;
16736396Ssklower				  u_int			e_cdt;
16836396Ssklower				}
16936396Ssklower AK_TPDU		{ u_int			e_cdt;
17036396Ssklower				  SeqNum 	 	e_seq;
17136396Ssklower				  SeqNum 	 	e_subseq;
17236396Ssklower				  u_char 	 	e_fcc_present;
17336396Ssklower				}
17436396Ssklower DT_TPDU		{ struct mbuf	*e_data; 	/* first field */
17536396Ssklower				  int 			e_datalen; /* 2nd field */
17636396Ssklower				  u_int 		e_eot;
17736396Ssklower				  SeqNum		e_seq;
17836396Ssklower				}
17936396Ssklower XPD_TPDU		{ struct mbuf 	*e_data;	/* first field */
18036396Ssklower				  int 			e_datalen; 	/* 2nd field */
18136396Ssklower				  SeqNum 		e_seq;
18236396Ssklower				}
18336396Ssklower XAK_TPDU		{ SeqNum 		e_seq;		}
18436396Ssklower
18536396Ssklower T_CONN_req
18636396Ssklower T_DISC_req		{ u_char		e_reason; 	}
18736396Ssklower T_LISTEN_req
18836396Ssklower T_DATA_req
18936396Ssklower T_XPD_req
19036396Ssklower T_USR_rcvd
19136396Ssklower T_USR_Xrcvd
19236396Ssklower T_DETACH
19336396Ssklower T_NETRESET
19438841Ssklower T_ACPT_req
19536396Ssklower
19636396Ssklower
19736396Ssklower*TRANSITIONS
19836396Ssklower
19936396Ssklower
20036396Ssklower/* TP_AKWAIT doesn't exist in TP 0 */
20136396SsklowerSAME			<==			TP_AKWAIT			[ CC_TPDU, DC_TPDU, XAK_TPDU ]
20236396Ssklower	DEFAULT
20336396Ssklower	NULLACTION
20436396Ssklower;
20536396Ssklower
20636396Ssklower
20736396Ssklower/* applicable in TP4, TP0 */
20836396SsklowerSAME			<==			TP_REFWAIT								DR_TPDU
20936396Ssklower	( $$.e_sref !=  0 )
21036396Ssklower	{
21136396Ssklower		(void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL);
21236396Ssklower	}
21336396Ssklower;
21436396Ssklower
21536396Ssklower/* applicable in TP4, TP0 */
21636396SsklowerSAME			<==			TP_REFWAIT			[ CR_TPDU, CC_TPDU, DT_TPDU,
21736396Ssklower					DR_TPDU, XPD_TPDU, AK_TPDU, XAK_TPDU, DC_TPDU, ER_TPDU ]
21836396Ssklower	DEFAULT
21936396Ssklower	{
22036396Ssklower#		ifdef TP_DEBUG
22136396Ssklower		if( $E.ev_number != AK_TPDU )
22236396Ssklower			printf("TPDU 0x%x in REFWAIT!!!!\n", $E.ev_number);
22336396Ssklower#		endif TP_DEBUG
22436396Ssklower	}
22536396Ssklower;
22636396Ssklower
22736396Ssklower/* applicable in TP4, TP0 */
22836396SsklowerSAME			<==			TP_REFWAIT				[ T_DETACH, T_DISC_req ]
22936396Ssklower	DEFAULT
23036396Ssklower	NULLACTION
23136396Ssklower;
23236396Ssklower
23336396Ssklower/* applicable in TP4, TP0 */
23436396SsklowerSAME			<==			TP_CRSENT								 AK_TPDU
23536396Ssklower	($P.tp_class == TP_CLASS_0)
23636396Ssklower	{
23736396Ssklower		/* oh, man is this grotesque or what? */
23836396Ssklower		(void) tp_goodack($P, $$.e_cdt, $$.e_seq,  $$.e_subseq);
23936396Ssklower		/* but it's necessary because this pseudo-ack may happen
24036396Ssklower		 * before the CC arrives, but we HAVE to adjust the
24136396Ssklower		 * snduna as a result of the ack, WHENEVER it arrives
24236396Ssklower		 */
24336396Ssklower	}
24436396Ssklower;
24536396Ssklower
24636396Ssklower/* applicable in TP4, TP0 */
24736396SsklowerSAME			<==			TP_CRSENT
24836396Ssklower					[ CR_TPDU, DC_TPDU, DT_TPDU, XPD_TPDU,  XAK_TPDU ]
24936396Ssklower	DEFAULT
25036396Ssklower	NULLACTION
25136396Ssklower;
25236396Ssklower
25336396Ssklower/* applicable in TP4, TP0 */
25436396SsklowerSAME			<==			TP_CLOSED					[ DT_TPDU, XPD_TPDU,
25536396Ssklower										ER_TPDU, DC_TPDU, AK_TPDU, XAK_TPDU ]
25636396Ssklower	DEFAULT
25736396Ssklower	NULLACTION
25836396Ssklower;
25936396Ssklower
26036396Ssklower/* TP_CLOSING doesn't exist in TP 0 */
26136396SsklowerSAME 			<== 		TP_CLOSING
26236396Ssklower					[ CC_TPDU, CR_TPDU, DT_TPDU, XPD_TPDU, AK_TPDU, XAK_TPDU ]
26336396Ssklower	DEFAULT
26436396Ssklower	NULLACTION
26536396Ssklower;
26636396Ssklower
26736396Ssklower
26836396Ssklower/* DC_TPDU doesn't exist in TP 0 */
26936396SsklowerSAME			<==			TP_OPEN						  DC_TPDU
27036396Ssklower	DEFAULT
27136396Ssklower	NULLACTION
27236396Ssklower;
27336396Ssklower
27436396Ssklower/* applicable in TP4, TP0 */
27536396SsklowerSAME			<==		 	TP_LISTENING  [DR_TPDU, CC_TPDU, DT_TPDU, XPD_TPDU,
27636396Ssklower										 ER_TPDU, DC_TPDU, AK_TPDU, XAK_TPDU ]
27736396Ssklower	DEFAULT
27836396Ssklower	NULLACTION
27936396Ssklower;
28036396Ssklower
28136396Ssklower/* applicable in TP4, TP0 */
28236396SsklowerTP_LISTENING	<==			TP_CLOSED  							T_LISTEN_req
28336396Ssklower	DEFAULT
28436396Ssklower	NULLACTION
28536396Ssklower;
28636396Ssklower
28736396Ssklower/* applicable in TP4, TP0 */
28836396SsklowerTP_CLOSED  		<== 		[ TP_LISTENING, TP_CLOSED ] 			T_DETACH
28936396Ssklower	DEFAULT
29036396Ssklower	{
29136396Ssklower		tp_detach($P);
29236396Ssklower	}
29336396Ssklower;
29436396Ssklower
29538841SsklowerTP_CONFIRMING	<==		 TP_LISTENING  								CR_TPDU
29638841Ssklower	( $P.tp_class == TP_CLASS_0)
29736396Ssklower	{
29836396Ssklower		$P.tp_refp->tpr_state = REF_OPEN; /* has timers ??? */
29936396Ssklower	}
30036396Ssklower;
30136396Ssklower
30238841SsklowerTP_CONFIRMING		<==		 TP_LISTENING  							CR_TPDU
30338841Ssklower	DEFAULT
30436396Ssklower	{
30536396Ssklower		IFTRACE(D_CONN)
30636396Ssklower			tptrace(TPPTmisc, "CR datalen data", $$.e_datalen, $$.e_data,0,0);
30736396Ssklower		ENDTRACE
30836396Ssklower		IFDEBUG(D_CONN)
30936396Ssklower			printf("CR datalen 0x%x data 0x%x", $$.e_datalen, $$.e_data);
31036396Ssklower		ENDDEBUG
31136396Ssklower		$P.tp_refp->tpr_state = REF_OPEN; /* has timers */
31238841Ssklower		$P.tp_fcredit = $$.e_cdt;
31336396Ssklower
31436396Ssklower		if ($$.e_datalen > 0) {
31536396Ssklower			/* n/a for class 0 */
31636396Ssklower			ASSERT($P.tp_Xrcv.sb_cc == 0);
31736396Ssklower			sbappendrecord(&$P.tp_Xrcv, $$.e_data);
31837469Ssklower			/*$P.tp_flags |= TPF_CONN_DATA_IN;*/
31936396Ssklower			$$.e_data = MNULL;
32036396Ssklower		}
32138841Ssklower	}
32238841Ssklower;
32338841Ssklower
32438841SsklowerTP_OPEN		<==		 TP_CONFIRMING  								T_ACPT_req
32538841Ssklower	( $P.tp_class == TP_CLASS_0 )
32638841Ssklower	{
32738841Ssklower		IncStat(ts_tp0_conn);
32838841Ssklower		IFTRACE(D_CONN)
32938841Ssklower			tptrace(TPPTmisc, "Confiming", $P, 0,0,0);
33038841Ssklower		ENDTRACE
33138841Ssklower		IFDEBUG(D_CONN)
33238841Ssklower			printf("Confirming connection: $P" );
33338841Ssklower		ENDDEBUG
33438841Ssklower		soisconnected($P.tp_sock);
33538841Ssklower		(void) tp_emit(CC_TPDU_type, $P, 0,0, MNULL) ;
33638841Ssklower		$P.tp_fcredit = 1;
33738841Ssklower	}
33838841Ssklower;
33938841Ssklower
34038841SsklowerTP_AKWAIT		<==		 TP_CONFIRMING  							T_ACPT_req
34138841Ssklower	(tp_emit(CC_TPDU_type, $P, 0,0, MCPY($P.tp_ucddata, M_NOWAIT)) == 0)
34238841Ssklower	{
34338841Ssklower		IncStat(ts_tp4_conn); /* even though not quite open */
34438841Ssklower		IFTRACE(D_CONN)
34538841Ssklower			tptrace(TPPTmisc, "Confiming", $P, 0,0,0);
34638841Ssklower		ENDTRACE
34738841Ssklower		IFDEBUG(D_CONN)
34838841Ssklower			printf("Confirming connection: $P" );
34938841Ssklower		ENDDEBUG
35038841Ssklower		soisconnecting($P.tp_sock);
35136396Ssklower		if($P.tp_rx_strat & TPRX_FASTSTART)
35238841Ssklower			$P.tp_cong_win = $P.tp_fcredit;
35336396Ssklower		$P.tp_retrans = $P.tp_Nretrans;
35436396Ssklower		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_cc_ticks);
35538841Ssklower	}
35636396Ssklower;
35736396Ssklower
35836396Ssklower/* TP4 only */
35938841SsklowerTP_CLOSED		<==		 TP_CONFIRMING								T_ACPT_req
36036396Ssklower	DEFAULT /* emit failed */
36136396Ssklower	{
36236396Ssklower		register struct tp_ref *r = $P.tp_refp;
36336396Ssklower
36436396Ssklower		IFDEBUG(D_CONN)
36536396Ssklower			printf("event: CR_TPDU emit CC failed done " );
36636396Ssklower		ENDDEBUG
36738841Ssklower		soisdisconnected($P.tp_sock);
36836396Ssklower		tp_recycle_tsuffix( $P );
36936396Ssklower		tp_freeref(r);
37036396Ssklower		tp_detach($P);
37136396Ssklower	}
37236396Ssklower;
37336396Ssklower
37436396Ssklower/* applicable in TP4, TP0 */
37536396SsklowerTP_CRSENT		<==		TP_CLOSED								T_CONN_req
37636396Ssklower	DEFAULT
37736396Ssklower	{
37836396Ssklower		int error;
37936396Ssklower		struct mbuf *data = MNULL;
38036396Ssklower
38136396Ssklower		IFTRACE(D_CONN)
38237469Ssklower			tptrace(TPPTmisc, "T_CONN_req flags ucddata", (int)$P.tp_flags,
38337469Ssklower			$P.tp_ucddata, 0, 0);
38436396Ssklower		ENDTRACE
38537469Ssklower		data =  MCPY($P.tp_ucddata, M_WAIT);
38637469Ssklower		if (data) {
38736396Ssklower			IFDEBUG(D_CONN)
38836396Ssklower				printf("T_CONN_req.trans m_copy cc 0x%x\n",
38937469Ssklower					$P.tp_ucddata);
39037469Ssklower				dump_mbuf(data, "sosnd @ T_CONN_req");
39136396Ssklower			ENDDEBUG
39236396Ssklower		}
39336396Ssklower
39436396Ssklower		if (error = tp_emit(CR_TPDU_type, $P, 0, 0, data) )
39536396Ssklower			return error; /* driver WON'T change state; will return error */
39636396Ssklower
39736396Ssklower		$P.tp_refp->tpr_state = REF_OPEN; /* has timers */
39836396Ssklower		if($P.tp_class != TP_CLASS_0) {
39936396Ssklower			$P.tp_retrans = $P.tp_Nretrans;
40036396Ssklower			tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_cr_ticks);
40136396Ssklower		}
40236396Ssklower	}
40336396Ssklower;
40436396Ssklower
40536396Ssklower/* applicable in TP4, TP0, but state TP_AKWAIT doesn't apply to TP0 */
40636396SsklowerTP_REFWAIT 		<==		[ TP_CRSENT, TP_AKWAIT, TP_OPEN ] 			DR_TPDU
40736396Ssklower	DEFAULT
40836396Ssklower	{
40936396Ssklower		if ($$.e_datalen > 0 && $P.tp_class != TP_CLASS_0) {
41037469Ssklower			/*sbdrop(&$P.tp_Xrcv, $P.tp_Xrcv.sb_cc); /* purge expedited data */
41137469Ssklower			sbflush(&$P.tp_Xrcv);
41236396Ssklower			$P.tp_flags |= TPF_DISC_DATA_IN;
41336396Ssklower			sbappendrecord(&$P.tp_Xrcv, $$.e_data);
41436396Ssklower			$$.e_data = MNULL;
41536396Ssklower		}
41636396Ssklower		tp_indicate(T_DISCONNECT, $P, TP_ERROR_MASK | (u_short)$$.e_reason);
41736396Ssklower		tp_soisdisconnected($P);
41836396Ssklower		if ($P.tp_class != TP_CLASS_0) {
41936396Ssklower			if ($P.tp_state == TP_OPEN ) {
42036396Ssklower				tp_euntimeout($P.tp_refp, TM_data_retrans); /* all */
42136396Ssklower				tp_cuntimeout($P.tp_refp, TM_retrans);
42236396Ssklower				tp_cuntimeout($P.tp_refp, TM_inact);
42336396Ssklower				tp_cuntimeout($P.tp_refp, TM_sendack);
42436396Ssklower			}
42536396Ssklower			tp_cuntimeout($P.tp_refp, TM_retrans);
42636396Ssklower			if( $$.e_sref !=  0 )
42736396Ssklower				(void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL);
42836396Ssklower		}
42936396Ssklower	}
43036396Ssklower;
43136396Ssklower
43236396SsklowerSAME 			<==		TP_CLOSED 									DR_TPDU
43336396Ssklower	DEFAULT
43436396Ssklower	{
43536396Ssklower		if( $$.e_sref != 0 )
43636396Ssklower			(void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL);
43736396Ssklower		/* reference timer already set - reset it to be safe (???) */
43836396Ssklower		tp_euntimeout($P.tp_refp, TM_reference); /* all */
43936396Ssklower		tp_etimeout($P.tp_refp, TM_reference, 0, 0, 0, (int)$P.tp_refer_ticks);
44036396Ssklower	}
44136396Ssklower;
44236396Ssklower
44336396Ssklower/* NBS(34) */
44436396SsklowerTP_REFWAIT 		<==  	TP_CRSENT  									ER_TPDU
44536396Ssklower	DEFAULT
44636396Ssklower	{
44736396Ssklower		tp_cuntimeout($P.tp_refp, TM_retrans);
44836396Ssklower		tp_indicate(T_DISCONNECT, $P,
44936396Ssklower			TP_ERROR_MASK |(u_short)($$.e_reason | 0x40));
45036396Ssklower		tp_soisdisconnected($P);
45136396Ssklower	}
45236396Ssklower;
45336396Ssklower
45436396Ssklower/* NBS(27) */
45536396SsklowerTP_REFWAIT		<==		TP_CLOSING									DR_TPDU
45636396Ssklower	DEFAULT
45736396Ssklower	{
45836396Ssklower		$P.tp_sock->so_error = (u_short)$$.e_reason;
45936396Ssklower		tp_cuntimeout($P.tp_refp, TM_retrans);
46036396Ssklower		tp_soisdisconnected($P);
46136396Ssklower	}
46236396Ssklower;
46336396Ssklower/* these two transitions are the same but can't be combined because xebec
46436396Ssklower * can't handle the use of $$.e_reason if they're combined
46536396Ssklower */
46636396Ssklower/* NBS(27) */
46736396SsklowerTP_REFWAIT		<==		TP_CLOSING									ER_TPDU
46836396Ssklower	DEFAULT
46936396Ssklower	{
47036396Ssklower		$P.tp_sock->so_error = (u_short)$$.e_reason;
47136396Ssklower		tp_cuntimeout($P.tp_refp, TM_retrans);
47236396Ssklower		tp_soisdisconnected($P);
47336396Ssklower	}
47436396Ssklower;
47536396Ssklower/* NBS(27) */
47636396SsklowerTP_REFWAIT		<==		TP_CLOSING									DC_TPDU
47736396Ssklower	DEFAULT
47836396Ssklower	{
47936396Ssklower		tp_cuntimeout($P.tp_refp, TM_retrans);
48036396Ssklower		tp_soisdisconnected($P);
48136396Ssklower	}
48236396Ssklower;
48336396Ssklower
48436396Ssklower/* NBS(21) */
48536396SsklowerSAME 			<== 	TP_CLOSED 						[ CC_TPDU, CR_TPDU ]
48636396Ssklower	DEFAULT
48736396Ssklower	{	/* don't ask me why we have to do this - spec says so */
48836396Ssklower		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_NO_SESSION, MNULL);
48936396Ssklower		/* don't bother with retransmissions of the DR */
49036396Ssklower	}
49136396Ssklower;
49236396Ssklower
49336396Ssklower/* NBS(34) */
49436396SsklowerTP_REFWAIT 		<== 	TP_OPEN  				 					ER_TPDU
49536396Ssklower	($P.tp_class == TP_CLASS_0)
49636396Ssklower	{
49736396Ssklower		tp_soisdisconnecting($P.tp_sock);
49836396Ssklower		tp_indicate(T_DISCONNECT, $P,
49936396Ssklower			TP_ERROR_MASK |(u_short)($$.e_reason | 0x40));
50036396Ssklower
50136396Ssklower		tp_soisdisconnected($P);
50236396Ssklower		tp_netcmd( $P, CONN_CLOSE );
50336396Ssklower	}
50436396Ssklower;
50536396Ssklower
50636396SsklowerTP_CLOSING 		<== 	[ TP_AKWAIT, TP_OPEN ]  					ER_TPDU
50736396Ssklower	DEFAULT
50836396Ssklower	{
50936396Ssklower		if ($P.tp_state == TP_OPEN) {
51036396Ssklower			tp_euntimeout($P.tp_refp, TM_data_retrans); /* all */
51136396Ssklower			tp_cuntimeout($P.tp_refp, TM_inact);
51236396Ssklower			tp_cuntimeout($P.tp_refp, TM_sendack);
51336396Ssklower		}
51436396Ssklower		tp_soisdisconnecting($P.tp_sock);
51536396Ssklower		tp_indicate(T_DISCONNECT, $P,
51636396Ssklower			TP_ERROR_MASK |(u_short)($$.e_reason | 0x40));
51736396Ssklower		$P.tp_retrans = $P.tp_Nretrans;
51836396Ssklower		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks);
51936396Ssklower		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_PROTO_ERR, MNULL);
52036396Ssklower	}
52136396Ssklower;
52236396Ssklower/* NBS(6) */
52336396SsklowerTP_OPEN			<==		TP_CRSENT									CC_TPDU
52436396Ssklower	($P.tp_class == TP_CLASS_0)
52536396Ssklower	{
52636396Ssklower		tp_cuntimeout($P.tp_refp, TM_retrans);
52736396Ssklower		IncStat(ts_tp0_conn);
52836396Ssklower		$P.tp_fcredit = 1;
52936396Ssklower		soisconnected($P.tp_sock);
53036396Ssklower	}
53136396Ssklower;
53236396Ssklower
53336396SsklowerTP_OPEN			<==		TP_CRSENT									CC_TPDU
53436396Ssklower	DEFAULT
53536396Ssklower	{
53636396Ssklower		IFDEBUG(D_CONN)
53736396Ssklower			printf("trans: CC_TPDU in CRSENT state flags 0x%x\n",
53836396Ssklower				(int)$P.tp_flags);
53936396Ssklower		ENDDEBUG
54036396Ssklower		IncStat(ts_tp4_conn);
54136396Ssklower		$P.tp_fref = $$.e_sref;
54236396Ssklower		$P.tp_fcredit = $$.e_cdt;
543*39921Ssklower		$P.tp_ackrcvd = 0;
54436396Ssklower		if($P.tp_rx_strat & TPRX_FASTSTART)
54536396Ssklower			$P.tp_cong_win = $$.e_cdt;
54636396Ssklower		tp_getoptions($P);
54736396Ssklower		tp_cuntimeout($P.tp_refp, TM_retrans);
54837469Ssklower		if ($P.tp_ucddata) {
54936396Ssklower			IFDEBUG(D_CONN)
55037469Ssklower				printf("dropping user connect data cc 0x%x\n",
55137469Ssklower					$P.tp_ucddata->m_len);
55236396Ssklower			ENDDEBUG
55337469Ssklower			m_freem($P.tp_ucddata);
55437469Ssklower			$P.tp_ucddata = 0;
55536396Ssklower		}
55636396Ssklower		soisconnected($P.tp_sock);
55736396Ssklower		if ($$.e_datalen > 0) {
55836396Ssklower			ASSERT($P.tp_Xrcv.sb_cc == 0); /* should be empty */
55936396Ssklower			sbappendrecord(&$P.tp_Xrcv, $$.e_data);
56036396Ssklower			$P.tp_flags |= TPF_CONN_DATA_IN;
56136396Ssklower			$$.e_data = MNULL;
56236396Ssklower		}
56336396Ssklower
56436396Ssklower		(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
56536396Ssklower		tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
56636396Ssklower	}
56736396Ssklower;
56836396Ssklower
56936396Ssklower/* TP4 only */
57036396SsklowerSAME			<==		TP_CRSENT									TM_retrans
57136396Ssklower	(	$P.tp_retrans > 0 )
57236396Ssklower	{
57336396Ssklower		struct mbuf *data = MNULL;
57436396Ssklower		int error;
57536396Ssklower
57636396Ssklower		IncStat(ts_retrans_cr);
577*39921Ssklower		$P.tp_cong_win = 1;
578*39921Ssklower		$P.tp_ackrcvd = 0;
57937469Ssklower		data = MCPY($P.tp_ucddata, M_NOWAIT);
58037469Ssklower		if($P.tp_ucddata) {
58136396Ssklower			IFDEBUG(D_CONN)
58237469Ssklower				printf("TM_retrans.trans m_copy cc 0x%x\n", data);
58337469Ssklower				dump_mbuf($P.tp_ucddata, "sosnd @ TM_retrans");
58436396Ssklower			ENDDEBUG
58536396Ssklower			if( data == MNULL )
58636396Ssklower				return ENOBUFS;
58736396Ssklower		}
58836396Ssklower
58936396Ssklower		$P.tp_retrans --;
59036396Ssklower		if( error = tp_emit(CR_TPDU_type, $P, 0, 0, data) ) {
59136396Ssklower			$P.tp_sock->so_error = error;
59236396Ssklower		}
59336396Ssklower		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_cr_ticks);
59436396Ssklower	}
59536396Ssklower;
59636396Ssklower
59736396Ssklower/* TP4 only  */
59836396SsklowerTP_REFWAIT		<==		TP_CRSENT									TM_retrans
59936396Ssklower	DEFAULT /* no more CR retransmissions */
60036396Ssklower	{
60136396Ssklower		IncStat(ts_conn_gaveup);
60236396Ssklower		$P.tp_sock->so_error = ETIMEDOUT;
60336396Ssklower		tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
60436396Ssklower		tp_soisdisconnected($P);
60536396Ssklower	}
60636396Ssklower;
60736396Ssklower
60836396Ssklower/* TP4 only */
60936396SsklowerSAME 			<==	 TP_AKWAIT											CR_TPDU
61036396Ssklower	DEFAULT
61136396Ssklower	/* duplicate CR (which doesn't really exist in the context of
61236396Ssklower	 * a connectionless network layer)
61336396Ssklower	 * Doesn't occur in class 0.
61436396Ssklower	 */
61536396Ssklower	{
61636396Ssklower		int error;
61737469Ssklower		struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
61836396Ssklower
61937469Ssklower		if( error = tp_emit(CC_TPDU_type, $P, 0, 0, data) ) {
62036396Ssklower			$P.tp_sock->so_error = error;
62136396Ssklower		}
62236396Ssklower		$P.tp_retrans = $P.tp_Nretrans;
62336396Ssklower		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_cc_ticks);
62436396Ssklower	}
62536396Ssklower;
62636396Ssklower
62736396Ssklower/* TP4 only */
62836396SsklowerTP_OPEN			<==		TP_AKWAIT 										DT_TPDU
62936396Ssklower	( IN_RWINDOW( $P, $$.e_seq,
63036396Ssklower					$P.tp_rcvnxt, SEQ($P, $P.tp_rcvnxt + $P.tp_lcredit)) )
63136396Ssklower	{
63236396Ssklower		int doack;
63336396Ssklower
63437469Ssklower		/*
63537469Ssklower		 * Get rid of any confirm or connect data, so that if we
63637469Ssklower		 * crash or close, it isn't thought of as disconnect data.
63737469Ssklower		 */
63837469Ssklower		if ($P.tp_ucddata) {
63937469Ssklower			m_freem($P.tp_ucddata);
64037469Ssklower			$P.tp_ucddata = 0;
64136396Ssklower		}
64236396Ssklower		tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
64336396Ssklower		tp_cuntimeout($P.tp_refp, TM_retrans);
64436396Ssklower		soisconnected($P.tp_sock);
64536396Ssklower		tp_getoptions($P);
64636396Ssklower		tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
64736396Ssklower
64836396Ssklower		/* see also next 2 transitions, if you make any changes */
64936396Ssklower
65036396Ssklower		doack = tp_stash($P, $E);
65136396Ssklower		IFDEBUG(D_DATA)
65236396Ssklower			printf("tp_stash returns %d\n",doack);
65336396Ssklower		ENDDEBUG
65436396Ssklower
65536396Ssklower		if(doack) {
65636396Ssklower			(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL );
65736396Ssklower			tp_ctimeout($P.tp_refp, TM_sendack, (int)$P.tp_keepalive_ticks);
65836396Ssklower		} else
65936396Ssklower			tp_ctimeout( $P.tp_refp, TM_sendack, (int)$P.tp_sendack_ticks);
66036396Ssklower
66136396Ssklower		IFDEBUG(D_DATA)
66236396Ssklower			printf("after stash calling sbwakeup\n");
66336396Ssklower		ENDDEBUG
66436396Ssklower	}
66536396Ssklower;
66636396Ssklower
66736396SsklowerSAME			<==		TP_OPEN 									DT_TPDU
66836396Ssklower	( $P.tp_class == TP_CLASS_0 )
66936396Ssklower	{
67036396Ssklower		tp0_stash($P, $E);
67136396Ssklower		sbwakeup( &$P.tp_sock->so_rcv );
67236396Ssklower
67336396Ssklower		IFDEBUG(D_DATA)
67436396Ssklower			printf("after stash calling sbwakeup\n");
67536396Ssklower		ENDDEBUG
67636396Ssklower	}
67736396Ssklower;
67836396Ssklower
67936396Ssklower/* TP4 only */
68036396SsklowerSAME			<==		TP_OPEN 									DT_TPDU
68136396Ssklower	( IN_RWINDOW( $P, $$.e_seq,
68236396Ssklower					$P.tp_rcvnxt, SEQ($P, $P.tp_rcvnxt + $P.tp_lcredit)) )
68336396Ssklower	{
68436396Ssklower		int doack; /* tells if we must ack immediately */
68536396Ssklower
68636396Ssklower		tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
68736396Ssklower		sbwakeup( &$P.tp_sock->so_rcv );
68836396Ssklower
68936396Ssklower		doack = tp_stash($P, $E);
69036396Ssklower		IFDEBUG(D_DATA)
69136396Ssklower			printf("tp_stash returns %d\n",doack);
69236396Ssklower		ENDDEBUG
69336396Ssklower
69436396Ssklower		if(doack)
69536396Ssklower			(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL );
69636396Ssklower		else
69736396Ssklower			tp_ctimeout_MIN( $P.tp_refp, TM_sendack, (int)$P.tp_sendack_ticks);
69836396Ssklower
69936396Ssklower		IFDEBUG(D_DATA)
70036396Ssklower			printf("after stash calling sbwakeup\n");
70136396Ssklower		ENDDEBUG
70236396Ssklower	}
70336396Ssklower;
70436396Ssklower
70536396Ssklower/* Not in window  - we must ack under certain circumstances, namely
70636396Ssklower * a) if the seq number is below lwe but > lwe - (max credit ever given)
70736396Ssklower * (to handle lost acks) Can use max-possible-credit for this ^^^.
70836396Ssklower * and
70936396Ssklower * b) seq number is > uwe but < uwe + previously sent & withdrawn credit
71036396Ssklower *
71136396Ssklower * (see 12.2.3.8.1 of ISO spec, p. 73)
71236396Ssklower * We just always ack.
71336396Ssklower */
71436396Ssklower/* TP4 only */
71536396SsklowerSAME 			<== 	[ TP_OPEN, TP_AKWAIT ]							DT_TPDU
71636396Ssklower	DEFAULT /* Not in window */
71736396Ssklower	{
71836396Ssklower		IFTRACE(D_DATA)
71936396Ssklower			tptrace(TPPTmisc, "NIW seq rcvnxt lcredit ",
72036396Ssklower				$$.e_seq, $P.tp_rcvnxt, $P.tp_lcredit, 0);
72136396Ssklower		ENDTRACE
72236396Ssklower		IncStat(ts_dt_niw);
72336396Ssklower		m_freem($$.e_data);
72436396Ssklower		tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
72536396Ssklower		(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL );
72636396Ssklower	}
72736396Ssklower;
72836396Ssklower
72936396Ssklower/* TP4 only */
73036396SsklowerTP_OPEN			<==		TP_AKWAIT										AK_TPDU
73136396Ssklower	DEFAULT
73236396Ssklower	{
73337469Ssklower		if ($P.tp_ucddata) {
73437469Ssklower			m_freem($P.tp_ucddata);
73537469Ssklower			$P.tp_ucddata = 0;
73636396Ssklower		}
73736396Ssklower		(void) tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq);
73836396Ssklower		tp_cuntimeout($P.tp_refp, TM_retrans);
73936396Ssklower
74036396Ssklower		tp_getoptions($P);
74136396Ssklower		soisconnected($P.tp_sock);
74236396Ssklower		IFTRACE(D_CONN)
74336396Ssklower			struct socket *so = $P.tp_sock;
74436396Ssklower			tptrace(TPPTmisc,
74536396Ssklower			"called sosiconn: so so_state rcv.sb_sel rcv.sb_flags",
74636396Ssklower				so, so->so_state, so->so_rcv.sb_sel, so->so_rcv.sb_flags);
74736396Ssklower			tptrace(TPPTmisc,
74836396Ssklower			"called sosiconn 2: so_qlen so_error so_rcv.sb_cc so_head",
74936396Ssklower				so->so_qlen, so->so_error, so->so_rcv.sb_cc, so->so_head);
75036396Ssklower		ENDTRACE
75136396Ssklower
75236396Ssklower		tp_ctimeout($P.tp_refp, TM_sendack, (int)$P.tp_keepalive_ticks);
75336396Ssklower		tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
75436396Ssklower	}
75536396Ssklower;
75636396Ssklower
75736396Ssklower/* TP4 only */
75836396SsklowerTP_OPEN 		<== 	[ TP_OPEN, TP_AKWAIT ]						XPD_TPDU
75937469Ssklower	( $P.tp_Xrcvnxt == $$.e_seq  /* && $P.tp_Xrcv.sb_cc == 0*/)
76036396Ssklower	{
76136396Ssklower		if( $P.tp_state == TP_AKWAIT ) {
76237469Ssklower			if ($P.tp_ucddata) {
76337469Ssklower				m_freem($P.tp_ucddata);
76437469Ssklower				$P.tp_ucddata = 0;
76536396Ssklower			}
76636396Ssklower			tp_cuntimeout($P.tp_refp, TM_retrans);
76736396Ssklower			tp_getoptions($P);
76836396Ssklower			soisconnected($P.tp_sock);
76936396Ssklower			tp_ctimeout($P.tp_refp, TM_sendack, (int)$P.tp_keepalive_ticks);
77036396Ssklower			tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
77136396Ssklower		}
77236396Ssklower		IFTRACE(D_XPD)
77336396Ssklower		tptrace(TPPTmisc, "XPD tpdu accepted Xrcvnxt, e_seq datalen m_len\n",
77436396Ssklower				$P.tp_Xrcvnxt,$$.e_seq,  $$.e_datalen, $$.e_data->m_len);
77536396Ssklower		ENDTRACE
77636396Ssklower
77737469Ssklower		$P.tp_sock->so_state |= SS_RCVATMARK;
77837469Ssklower		sbinsertoob(&$P.tp_Xrcv, $$.e_data);
77936396Ssklower		IFDEBUG(D_XPD)
78036396Ssklower			dump_mbuf($$.e_data, "XPD TPDU: tp_Xrcv");
78136396Ssklower		ENDDEBUG
78236396Ssklower		tp_indicate(T_XDATA, $P, 0);
78336396Ssklower		sbwakeup( &$P.tp_Xrcv );
78436396Ssklower
78536396Ssklower		(void) tp_emit(XAK_TPDU_type, $P, $P.tp_Xrcvnxt, 0, MNULL);
78636396Ssklower		SEQ_INC($P, $P.tp_Xrcvnxt);
78736396Ssklower	}
78836396Ssklower;
78936396Ssklower
79036396Ssklower/* TP4 only */
79136396SsklowerSAME			<==		TP_OPEN 									T_USR_Xrcvd
79236396Ssklower	DEFAULT
79336396Ssklower	{
79436396Ssklower		if( $P.tp_Xrcv.sb_cc == 0 ) {
79537469Ssklower			/*$P.tp_flags &= ~TPF_XPD_PRESENT;*/
79636396Ssklower			/* kludge for select(): */
79737469Ssklower			/* $P.tp_sock->so_state &= ~SS_OOBAVAIL; */
79836396Ssklower		}
79936396Ssklower	}
80036396Ssklower	/* OLD WAY:
80136396Ssklower	 * Ack only after the user receives the XPD.  This is better for
80236396Ssklower	 * users that use one XPD right after another.
80336396Ssklower	 * Acking right away (the NEW WAY, see the prev. transition) is
80436396Ssklower	 * better for occasional * XPD, when the receiving user doesn't
80536396Ssklower	 * want to read the XPD immediately (which is session's behavior).
80636396Ssklower	 *
80736396Ssklower		int error = tp_emit(XAK_TPDU_type, $P, $P.tp_Xrcvnxt, 0, MNULL);
80836396Ssklower		SEQ_INC($P, $P.tp_Xrcvnxt);
80936396Ssklower		return error;
81036396Ssklower	*/
81136396Ssklower;
81236396Ssklower
81336396Ssklower/* NOTE: presently if the user doesn't read the connection data
81436396Ssklower * before and expedited data PDU comes in, the connection data will
81536396Ssklower * be dropped. This is a bug.  To avoid it, we need somewhere else
81636396Ssklower * to put the connection data.
81736396Ssklower * On the other hand, we need not to have it sitting around forever.
81836396Ssklower * This is a problem with the idea of trying to accommodate
81936396Ssklower * data on connect w/ a passive-open user interface.
82036396Ssklower */
82136396Ssklower/* TP4 only */
82236396Ssklower
82336396SsklowerSAME	 		<== 	[ TP_AKWAIT, TP_OPEN ] 							XPD_TPDU
82436396Ssklower	DEFAULT /* not in window or cdt==0 */
82536396Ssklower	{
82636396Ssklower		IFTRACE(D_XPD)
82736396Ssklower			tptrace(TPPTmisc, "XPD tpdu niw (Xrcvnxt, e_seq) or not cdt (cc)\n",
82836396Ssklower				$P.tp_Xrcvnxt, $$.e_seq,  $P.tp_Xrcv.sb_cc , 0);
82936396Ssklower		ENDTRACE
83036396Ssklower		if( $P.tp_Xrcvnxt != $$.e_seq )
83136396Ssklower			IncStat(ts_xpd_niw);
83236396Ssklower		if( $P.tp_Xrcv.sb_cc ) {
83337469Ssklower#ifdef notdef
83436396Ssklower			if( $P.tp_flags & TPF_CONN_DATA_IN ) {
83536396Ssklower				/* user isn't reading the connection data; see note above */
83636396Ssklower				sbdrop(&$P.tp_Xrcv, $P.tp_Xrcv.sb_cc);
83736396Ssklower				$P.tp_flags &= ~TPF_CONN_DATA_IN;
83836396Ssklower			}
83937469Ssklower#endif notdef
84036396Ssklower			/* might as well kick 'em again */
84136396Ssklower			tp_indicate(T_XDATA, $P, 0);
84236396Ssklower			IncStat(ts_xpd_dup);
84336396Ssklower		}
84436396Ssklower		m_freem($$.e_data);
84536396Ssklower		tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
84636396Ssklower		/* don't send an xack because the xak gives "last one received", not
84736396Ssklower		 * "next one i expect" (dumb)
84836396Ssklower		 */
84936396Ssklower	}
85036396Ssklower;
85136396Ssklower
85236396Ssklower/* Occurs (AKWAIT, OPEN) when parent (listening) socket gets aborted, and tries
85336396Ssklower * to detach all its "children"
85436396Ssklower * Also (CRSENT) when user kills a job that's doing a connect()
85536396Ssklower */
85636396SsklowerTP_REFWAIT		<== 	TP_CRSENT 										T_DETACH
85736396Ssklower	($P.tp_class == TP_CLASS_0)
85836396Ssklower	{
85936396Ssklower		struct socket *so = $P.tp_sock;
86036396Ssklower
86136396Ssklower		/* detach from parent socket so it can finish closing */
86236396Ssklower		if (so->so_head) {
86336396Ssklower			if (!soqremque(so, 0) && !soqremque(so, 1))
86436396Ssklower				panic("tp: T_DETACH");
86536396Ssklower			so->so_head = 0;
86636396Ssklower		}
86736396Ssklower		tp_soisdisconnecting($P.tp_sock);
86836396Ssklower		tp_netcmd( $P, CONN_CLOSE);
86936396Ssklower		tp_soisdisconnected($P);
87036396Ssklower	}
87136396Ssklower;
87236396Ssklower
87336396Ssklower/* TP4 only */
87438841SsklowerTP_CLOSING		<== [ TP_CLOSING, TP_AKWAIT, TP_CRSENT, TP_CONFIRMING ]	T_DETACH
87536396Ssklower	DEFAULT
87636396Ssklower	{
87736396Ssklower		struct socket *so = $P.tp_sock;
87837469Ssklower		struct mbuf *data = MNULL;
87936396Ssklower
88036396Ssklower		/* detach from parent socket so it can finish closing */
88136396Ssklower		if (so->so_head) {
88236396Ssklower			if (!soqremque(so, 0) && !soqremque(so, 1))
88336396Ssklower				panic("tp: T_DETACH");
88436396Ssklower			so->so_head = 0;
88536396Ssklower		}
88636396Ssklower		if ($P.tp_state != TP_CLOSING) {
88736396Ssklower			tp_soisdisconnecting($P.tp_sock);
88837469Ssklower			data = MCPY($P.tp_ucddata, M_NOWAIT);
88937469Ssklower			(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_NORMAL_DISC, data);
89036396Ssklower			$P.tp_retrans = $P.tp_Nretrans;
89136396Ssklower			tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks);
89236396Ssklower		}
89336396Ssklower	}
89436396Ssklower;
89536396Ssklower
89636396SsklowerTP_REFWAIT		<==		[ TP_OPEN, TP_CRSENT ]		 	  			T_DISC_req
89736396Ssklower	( $P.tp_class == TP_CLASS_0 )
89836396Ssklower	{
89936396Ssklower		tp_soisdisconnecting($P.tp_sock);
90036396Ssklower		tp_netcmd( $P, CONN_CLOSE);
90136396Ssklower		tp_soisdisconnected($P);
90236396Ssklower	}
90336396Ssklower;
90436396Ssklower
90536396Ssklower/* TP4 only */
90638841SsklowerTP_CLOSING		<==	[ TP_AKWAIT, TP_OPEN, TP_CRSENT, TP_CONFIRMING ]  T_DISC_req
90736396Ssklower	DEFAULT
90836396Ssklower	{
90937469Ssklower		struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
91036396Ssklower
91136396Ssklower		if($P.tp_state == TP_OPEN) {
91236396Ssklower			tp_euntimeout($P.tp_refp, TM_data_retrans); /* all */
91336396Ssklower			tp_cuntimeout($P.tp_refp, TM_inact);
91436396Ssklower			tp_cuntimeout($P.tp_refp, TM_sendack);
91536396Ssklower		}
91637469Ssklower		if (data) {
91736396Ssklower			IFDEBUG(D_CONN)
91837469Ssklower				printf("T_DISC_req.trans tp_ucddata 0x%x\n",
91937469Ssklower					$P.tp_ucddata);
92037469Ssklower				dump_mbuf(data, "ucddata @ T_DISC_req");
92136396Ssklower			ENDDEBUG
92236396Ssklower		}
92336396Ssklower		tp_soisdisconnecting($P.tp_sock);
92436396Ssklower		$P.tp_retrans = $P.tp_Nretrans;
92536396Ssklower		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks);
92636396Ssklower
92736396Ssklower		if( trick_hc )
92836396Ssklower			return tp_emit(DR_TPDU_type, $P, 0, $$.e_reason, data);
92936396Ssklower	}
93036396Ssklower;
93136396Ssklower
93236396Ssklower/* TP4 only */
93336396SsklowerSAME			<==		TP_AKWAIT									TM_retrans
93436396Ssklower	( $P.tp_retrans > 0 )
93536396Ssklower	{
93636396Ssklower		int error;
93737469Ssklower		struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
93836396Ssklower
93936396Ssklower		IncStat(ts_retrans_cc);
94036396Ssklower		$P.tp_retrans --;
941*39921Ssklower		$P.tp_cong_win = 1;
942*39921Ssklower		$P.tp_ackrcvd = 0;
943*39921Ssklower
94437469Ssklower		if( error = tp_emit(CC_TPDU_type, $P, 0, 0, data) )
94536396Ssklower			$P.tp_sock->so_error = error;
94636396Ssklower		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_cc_ticks);
94736396Ssklower	}
94836396Ssklower;
94936396Ssklower
95036396Ssklower/* TP4 only */
95136396SsklowerTP_CLOSING		<==		TP_AKWAIT									TM_retrans
95236396Ssklower	DEFAULT  /* out of time */
95336396Ssklower	{
95436396Ssklower		IncStat(ts_conn_gaveup);
95536396Ssklower		tp_soisdisconnecting($P.tp_sock);
95636396Ssklower		$P.tp_sock->so_error = ETIMEDOUT;
95736396Ssklower		tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
95836396Ssklower		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_CONGEST, MNULL);
95936396Ssklower		$P.tp_retrans = $P.tp_Nretrans;
96036396Ssklower		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks);
96136396Ssklower	}
96236396Ssklower;
96336396Ssklower
96436396Ssklower/* the retrans timers had better go off BEFORE the inactivity timer does,
96536396Ssklower * if transmissions are going on.
96636396Ssklower * (i.e., TM_inact should be greater than timer for all retrans plus ack
96736396Ssklower * turnaround)
96836396Ssklower */
96936396Ssklower/* TP4 only */
97036396SsklowerTP_CLOSING 		<==		TP_OPEN		   [ TM_inact, TM_retrans, TM_data_retrans ]
97136396Ssklower	DEFAULT
97236396Ssklower	{
97336396Ssklower		tp_euntimeout($P.tp_refp, TM_data_retrans); /* all */
97436396Ssklower		tp_cuntimeout($P.tp_refp, TM_inact);
97536396Ssklower		tp_cuntimeout($P.tp_refp, TM_sendack);
97636396Ssklower
97736396Ssklower		IncStat(ts_conn_gaveup);
97836396Ssklower		tp_soisdisconnecting($P.tp_sock);
97936396Ssklower		$P.tp_sock->so_error = ETIMEDOUT;
98036396Ssklower		tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
98136396Ssklower		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_CONGEST_2, MNULL);
98236396Ssklower		$P.tp_retrans = $P.tp_Nretrans;
98336396Ssklower		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks);
98436396Ssklower	}
98536396Ssklower;
98636396Ssklower
98736396Ssklower/* TP4 only */
98836396SsklowerSAME			<==		TP_OPEN										TM_retrans
98936396Ssklower	( $P.tp_retrans > 0 )
99036396Ssklower	{
991*39921Ssklower		$P.tp_cong_win = 1;
992*39921Ssklower		$P.tp_ackrcvd = 0;
99336396Ssklower		/* resume XPD */
99436396Ssklower		if	( $P.tp_Xsnd.sb_mb )  {
99537469Ssklower			struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc);
99636396Ssklower			/* m_copy doesn't preserve the m_xlink field, but at this pt.
99736396Ssklower			 * that doesn't matter
99836396Ssklower			 */
99936396Ssklower
100036396Ssklower			IFTRACE(D_XPD)
100136396Ssklower				tptrace(TPPTmisc, "XPD retrans: Xuna Xsndnxt sndhiwat snduna",
100236396Ssklower					$P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndhiwat,
100336396Ssklower					$P.tp_snduna);
100436396Ssklower			ENDTRACE
100536396Ssklower			IFDEBUG(D_XPD)
100636396Ssklower				dump_mbuf(m, "XPD retrans emitting M");
100736396Ssklower			ENDDEBUG
100836396Ssklower			IncStat(ts_retrans_xpd);
100936396Ssklower			$P.tp_retrans --;
101036396Ssklower			(void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
101136396Ssklower			tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_xpd_ticks);
101236396Ssklower		}
101336396Ssklower	}
101436396Ssklower;
101536396Ssklower
101636396Ssklower/* TP4 only */
101736396SsklowerSAME 			<==		TP_OPEN									TM_data_retrans
101836396Ssklower	( $$.e_retrans > 0 )
101936396Ssklower	{
102036396Ssklower		register 	SeqNum			low, lowsave = 0;
102136396Ssklower		register	struct tp_rtc 	*r = $P.tp_snduna_rtc;
102236396Ssklower		register	struct mbuf 	*m;
102336396Ssklower		register	SeqNum			high = $$.e_high;
102436396Ssklower
1025*39921Ssklower		low = $P.tp_snduna;
1026*39921Ssklower		lowsave = high = low;
1027*39921Ssklower
1028*39921Ssklower		tp_euntimeout_lss($P.tp_refp, TM_data_retrans,
1029*39921Ssklower			SEQ_ADD($P, $P.tp_sndhiwat, 1));
1030*39921Ssklower		$P.tp_retrans_hiwat = $P.tp_sndhiwat;
1031*39921Ssklower
103236396Ssklower		if (($P.tp_rx_strat & TPRX_EACH) == 0)
103336396Ssklower			high = (high>low)?low:high;
103436396Ssklower
103536396Ssklower		if( $P.tp_rx_strat & TPRX_USE_CW ) {
103636396Ssklower			register int i;
103736396Ssklower
103836396Ssklower			$P.tp_cong_win = 1;
1039*39921Ssklower			$P.tp_ackrcvd = 0;
1040*39921Ssklower			i = SEQ_ADD($P, low, $P.tp_cong_win);
104136396Ssklower
1042*39921Ssklower			high = SEQ_MIN($P, high, $P.tp_sndhiwat);
1043*39921Ssklower
104436396Ssklower		}
104536396Ssklower
104636396Ssklower		while( SEQ_LEQ($P, low, high) ){
104736396Ssklower			if ( r == (struct tp_rtc *)0 ){
104836396Ssklower				IFDEBUG(D_RTC)
104936396Ssklower					printf( "tp: retrans rtc list is GONE!\n");
105036396Ssklower				ENDDEBUG
105136396Ssklower				break;
105236396Ssklower			}
105336396Ssklower			if ( r->tprt_seq == low ){
105436396Ssklower				if(( m = m_copy(r->tprt_data, 0, r->tprt_octets ))== MNULL)
105536396Ssklower					break;
105636396Ssklower				(void) tp_emit(DT_TPDU_type, $P, low, r->tprt_eot, m);
105736396Ssklower				IncStat(ts_retrans_dt);
105836396Ssklower				SEQ_INC($P, low );
105936396Ssklower			}
106036396Ssklower			r = r->tprt_next;
106136396Ssklower		}
1062*39921Ssklower/* CE_BIT
106336396Ssklower		if ( SEQ_LEQ($P, lowsave, high) ){
1064*39921Ssklower*/
106536396Ssklower			$$.e_retrans --;
106636396Ssklower			tp_etimeout($P.tp_refp, TM_data_retrans, (caddr_t)lowsave,
106736396Ssklower					(caddr_t)high, $$.e_retrans,
106836396Ssklower					($P.tp_Nretrans - $$.e_retrans) * (int)$P.tp_dt_ticks);
1069*39921Ssklower/* CE_BIT
107036396Ssklower		}
1071*39921Ssklower*/
107236396Ssklower	}
107336396Ssklower;
107436396Ssklower
107536396Ssklower/* TP4 only */
107636396SsklowerSAME	 		<==		TP_CLOSING									TM_retrans
107736396Ssklower	(	$P.tp_retrans > 0 )
107836396Ssklower	{
107936396Ssklower		$P.tp_retrans --;
108036396Ssklower		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_DR_NO_REAS, MNULL);
108136396Ssklower		IncStat(ts_retrans_dr);
108236396Ssklower		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks);
108336396Ssklower	}
108436396Ssklower;
108536396Ssklower
108636396Ssklower/* TP4 only */
108736396SsklowerTP_REFWAIT 		<==		TP_CLOSING									TM_retrans
108836396Ssklower	DEFAULT	/* no more retrans - gave up */
108936396Ssklower	{
109036396Ssklower		$P.tp_sock->so_error = ETIMEDOUT;
109136396Ssklower		$P.tp_refp->tpr_state = REF_FROZEN;
109236396Ssklower		tp_recycle_tsuffix( $P );
109336396Ssklower		tp_etimeout($P.tp_refp, TM_reference, 0,0,0, (int)$P.tp_refer_ticks);
109436396Ssklower	}
109536396Ssklower;
109636396Ssklower
109736396Ssklower/*
109836396Ssklower * The resources are kept around until the ref timer goes off.
109936396Ssklower * The suffices are wiped out sooner so they can be reused right away.
110036396Ssklower */
110136396Ssklower/* applicable in TP4, TP0 */
110236396SsklowerTP_CLOSED 		<==		TP_REFWAIT 									TM_reference
110336396Ssklower	DEFAULT
110436396Ssklower	{
110536396Ssklower		tp_freeref($P.tp_refp);
110636396Ssklower		tp_detach($P);
110736396Ssklower	}
110836396Ssklower;
110936396Ssklower
111036396Ssklower/* applicable in TP4, TP0 */
111136396Ssklower/* A duplicate CR from connectionless network layer can't happen */
111236396SsklowerSAME 			<== 	TP_OPEN 							[ CR_TPDU, CC_TPDU ]
111336396Ssklower	DEFAULT
111436396Ssklower	{
111536396Ssklower		if( $P.tp_class != TP_CLASS_0) {
111636396Ssklower			tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
111736396Ssklower			if ( $E.ev_number == CC_TPDU )
111836396Ssklower				(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
111936396Ssklower		}
112036396Ssklower		/* ignore it if class 0 - state tables are blank for this */
112136396Ssklower	}
112236396Ssklower;
112336396Ssklower
112436396Ssklower/* applicable in TP4, TP0 */
112536396SsklowerSAME			<== 	TP_OPEN									T_DATA_req
112636396Ssklower	DEFAULT
112736396Ssklower	{
112836396Ssklower		IFTRACE(D_DATA)
112936396Ssklower			tptrace(TPPTmisc, "T_DATA_req sndhiwat snduna fcredit, tpcb",
113036396Ssklower				$P.tp_sndhiwat, $P.tp_snduna, $P.tp_fcredit, $P);
113136396Ssklower		ENDTRACE
113236396Ssklower
113336396Ssklower		tp_send($P);
113436396Ssklower	}
113536396Ssklower;
113636396Ssklower
113736396Ssklower/* TP4 only */
113836396SsklowerSAME			<==		TP_OPEN										T_XPD_req
113936396Ssklower	DEFAULT
114036396Ssklower		/* T_XPD_req was issued by sosend iff xpd socket buf was empty
114136396Ssklower		 * at time of sosend(),
114236396Ssklower		 * AND (which means) there were no unacknowledged XPD tpdus outstanding!
114336396Ssklower		 */
114436396Ssklower	{
114536396Ssklower		int error = 0;
114636396Ssklower
114736396Ssklower		/* resume XPD */
114836396Ssklower		if	( $P.tp_Xsnd.sb_mb )  {
114937469Ssklower			struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc);
115036396Ssklower			/* m_copy doesn't preserve the m_xlink field, but at this pt.
115136396Ssklower			 * that doesn't matter
115236396Ssklower			 */
115336396Ssklower
115436396Ssklower			IFTRACE(D_XPD)
115536396Ssklower				tptrace(TPPTmisc, "XPD req: Xuna Xsndnxt sndhiwat snduna",
115636396Ssklower					$P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndhiwat,
115736396Ssklower					$P.tp_snduna);
115836396Ssklower			ENDTRACE
115936396Ssklower			IFDEBUG(D_XPD)
116036396Ssklower				printf("T_XPD_req: sb_cc 0x%x\n", $P.tp_Xsnd.sb_cc);
116136396Ssklower				dump_mbuf(m, "XPD req emitting M");
116236396Ssklower			ENDDEBUG
116336396Ssklower			error =
116436396Ssklower				tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
116536396Ssklower			$P.tp_retrans = $P.tp_Nretrans;
116636396Ssklower			tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_xpd_ticks);
116736396Ssklower			SEQ_INC($P, $P.tp_Xsndnxt);
116836396Ssklower		}
116936396Ssklower		if(trick_hc)
117036396Ssklower			return error;
117136396Ssklower	}
117236396Ssklower;
117336396Ssklower
117436396Ssklower/* TP4, faked ack in TP0 when cons send completes */
117536396SsklowerSAME 			<==		TP_OPEN 									AK_TPDU
117636396Ssklower	( tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq)  )
117736396Ssklower
117836396Ssklower	/* tp_goodack == true means
117936396Ssklower	 * EITHER it actually acked something heretofore unacknowledged
118036396Ssklower	 * OR no news but the credit should be processed.
118136396Ssklower	 */
118236396Ssklower	{
118336396Ssklower		IFDEBUG(D_ACKRECV)
118436396Ssklower			printf("GOOD ACK seq 0x%x cdt 0x%x\n", $$.e_seq, $$.e_cdt);
118536396Ssklower		ENDDEBUG
118636396Ssklower		if( $P.tp_class != TP_CLASS_0) {
118736396Ssklower			tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
118836396Ssklower			tp_euntimeout_lss($P.tp_refp, TM_data_retrans, $$.e_seq);
118936396Ssklower		}
119036396Ssklower		sbwakeup( &$P.tp_sock->so_snd );
119136396Ssklower
1192*39921Ssklower		if ($P.tp_sndhiwat <= $P.tp_retrans_hiwat &&
1193*39921Ssklower			$P.tp_snduna <= $P.tp_retrans_hiwat) {
1194*39921Ssklower
1195*39921Ssklower			register    struct mbuf     *m;
1196*39921Ssklower			/* extern      struct mbuf     *m_copy(); */
1197*39921Ssklower			register    struct tp_rtc   *r;
1198*39921Ssklower			SeqNum      high, retrans, low_save;
1199*39921Ssklower
1200*39921Ssklower			high = SEQ_MIN($P, SEQ_ADD($P, $P.tp_snduna,
1201*39921Ssklower					MIN($P.tp_cong_win, $P.tp_fcredit)) - 1,
1202*39921Ssklower					$P.tp_sndhiwat);
1203*39921Ssklower			low_save = retrans = SEQ_MAX($P, SEQ_ADD($P, $P.tp_last_retrans, 1),
1204*39921Ssklower					$P.tp_snduna);
1205*39921Ssklower			for (; SEQ_LEQ($P, retrans, high); SEQ_INC($P, retrans)) {
1206*39921Ssklower
1207*39921Ssklower				for (r = $P.tp_snduna_rtc; r; r = r->tprt_next){
1208*39921Ssklower					if ( r->tprt_seq == retrans ){
1209*39921Ssklower						if(( m = m_copy(r->tprt_data, 0, r->tprt_octets ))
1210*39921Ssklower								== MNULL)
1211*39921Ssklower							break;
1212*39921Ssklower						(void) tp_emit(DT_TPDU_type, $P, retrans,
1213*39921Ssklower							r->tprt_eot, m);
1214*39921Ssklower						$P.tp_last_retrans = retrans;
1215*39921Ssklower						IncStat(ts_retrans_dt);
1216*39921Ssklower						break;
1217*39921Ssklower					}
1218*39921Ssklower				}
1219*39921Ssklower				if ( r == (struct tp_rtc *)0 ){
1220*39921Ssklower					IFDEBUG(D_RTC)
1221*39921Ssklower						printf( "tp: retrans rtc list is GONE!\n");
1222*39921Ssklower					ENDDEBUG
1223*39921Ssklower					break;
1224*39921Ssklower				}
1225*39921Ssklower			}
1226*39921Ssklower			tp_etimeout($P.tp_refp, TM_data_retrans, (caddr_t)low_save,
1227*39921Ssklower					(caddr_t)high, $P.tp_retrans, (int)$P.tp_dt_ticks);
1228*39921Ssklower			if (SEQ_DEC($P, retrans) == $P.tp_retrans_hiwat)
1229*39921Ssklower				tp_send($P);
1230*39921Ssklower		}
1231*39921Ssklower		else {
1232*39921Ssklower			tp_send($P);
1233*39921Ssklower		}
123436396Ssklower		IFDEBUG(D_ACKRECV)
123536396Ssklower			printf("GOOD ACK new sndhiwat 0x%x\n", $P.tp_sndhiwat);
123636396Ssklower		ENDDEBUG
123736396Ssklower	}
123836396Ssklower;
123936396Ssklower
124036396Ssklower/* TP4, and TP0 after sending a CC or possibly a CR */
124136396SsklowerSAME			<==		TP_OPEN 			 						XAK_TPDU
124236396Ssklower	DEFAULT
124336396Ssklower	{
124436396Ssklower		IFTRACE(D_ACKRECV)
124536396Ssklower			tptrace(TPPTmisc, "BOGUS XACK eventtype ", $E.ev_number, 0, 0,0);
124636396Ssklower		ENDTRACE
124736396Ssklower		if( $P.tp_class != TP_CLASS_0 ) {
124836396Ssklower			tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
124936396Ssklower		}
125036396Ssklower	}
125136396Ssklower;
125236396Ssklower/* TP4, and TP0 after sending a CC or possibly a CR */
125336396SsklowerSAME			<==		TP_OPEN 			 						 AK_TPDU
125436396Ssklower	DEFAULT
125536396Ssklower	{
125636396Ssklower		IFTRACE(D_ACKRECV)
125736396Ssklower			tptrace(TPPTmisc, "BOGUS ACK fcc_present, tp_r_subseq e_subseq",
125836396Ssklower				$$.e_fcc_present, $P.tp_r_subseq, $$.e_subseq, 0);
125936396Ssklower		ENDTRACE
126036396Ssklower		if( $P.tp_class != TP_CLASS_0 ) {
126136396Ssklower
126236396Ssklower			if ( !$$.e_fcc_present ) {
126336396Ssklower				/* send ACK with FCC */
126436396Ssklower				IncStat( ts_ackreason[_ACK_FCC_] );
126536396Ssklower				(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 1, MNULL);
126636396Ssklower			}
126736396Ssklower			tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
126836396Ssklower		}
126936396Ssklower	}
127036396Ssklower;
127136396Ssklower
127236396Ssklower/* NBS(47) */
127336396Ssklower	/* goes in at *** */
127436396Ssklower		/* just so happens that this is never true now, because we allow
127536396Ssklower		 * only 1 packet in the queue at once (this could be changed)
127636396Ssklower		if	( $P.tp_Xsnd.sb_mb )  {
127736396Ssklower			struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, ??);
127836396Ssklower
127936396Ssklower			(void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
128036396Ssklower			$P.tp_retrans = $P.tp_Nretrans;
128136396Ssklower			tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_xpd_ticks);
128236396Ssklower			SEQ_INC($P, $P.tp_Xsndnxt);
128336396Ssklower		}
128436396Ssklower		 */
128536396Ssklower	/* end of the above hack */
128636396Ssklower
128736396Ssklower/* TP4 only */
128836396SsklowerSAME			<== 	TP_OPEN 										XAK_TPDU
128936396Ssklower	( tp_goodXack($P, $$.e_seq) )
129036396Ssklower	/* tp_goodXack checks for good ack, removes the correct
129136396Ssklower	 * tpdu from the queue and  returns 1 if ack was legit, 0 if not.
129236396Ssklower	 * also updates tp_Xuna
129336396Ssklower	 */
129436396Ssklower	{
129536396Ssklower		tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
129636396Ssklower		tp_cuntimeout($P.tp_refp, TM_retrans);
129736396Ssklower
129836396Ssklower		sbwakeup( &$P.tp_sock->so_snd );
129936396Ssklower
130036396Ssklower		/* resume normal data */
130136396Ssklower		tp_send($P);
130236396Ssklower	}
130336396Ssklower;
130436396Ssklower
130536396Ssklower/* TP4 only */
130636396SsklowerSAME			<==		TP_OPEN 								TM_sendack
130736396Ssklower	DEFAULT
130836396Ssklower	{
130936396Ssklower		IFTRACE(D_TIMER)
131036396Ssklower			tptrace(TPPTsendack, -1, $P.tp_lcredit, $P.tp_sent_uwe,
131136396Ssklower			$P.tp_sent_lcdt, 0);
131236396Ssklower		ENDTRACE
131336396Ssklower		IncPStat($P, tps_n_TMsendack);
131436396Ssklower		(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
131536396Ssklower	}
131636396Ssklower;
131736396Ssklower
131836396Ssklower/* TP0 only */
131936396SsklowerSAME			<==		TP_OPEN 									T_USR_rcvd
132036396Ssklower	($P.tp_class == TP_CLASS_0)
132136396Ssklower	NULLACTION
132236396Ssklower;
132336396Ssklower
132436396Ssklower/* TP4 only */
132536396Ssklower		/* If old credit was zero,
132636396Ssklower		 * we'd better inform other side that we now have space
132736396Ssklower		 * But this is not enough.  Sender might not yet have
132836396Ssklower		 * seen an ack with cdt 0 but it might still think the
132936396Ssklower		 * window is closed, so it's going to wait.
133036396Ssklower		 * Best to send an ack each time.
133136396Ssklower		 * Strictly speaking, this ought to be a function of the
133236396Ssklower		 * general ack strategy.
133336396Ssklower		 */
133436396SsklowerSAME			<==		TP_OPEN 									T_USR_rcvd
133536396Ssklower	DEFAULT
133636396Ssklower	{
133736396Ssklower		if( trick_hc ) {
133836396Ssklower			IncStat(ts_ackreason[_ACK_USRRCV_]);
1339*39921Ssklower
1340*39921Ssklower			/* send an ACK only if there's new information */
1341*39921Ssklower			LOCAL_CREDIT( $P );
1342*39921Ssklower			if (($P.tp_rcvnxt != $P.tp_sent_rcvnxt) ||
1343*39921Ssklower				($P.tp_lcredit != $P.tp_sent_lcdt))
1344*39921Ssklower
1345*39921Ssklower				return tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
134636396Ssklower		}
134736396Ssklower	}
134836396Ssklower;
134936396Ssklower
135036396Ssklower/* applicable in TP4, TP0 */
135136396SsklowerSAME			<==		TP_REFWAIT 				[ T_USR_rcvd, T_USR_Xrcvd ]
135236396Ssklower	DEFAULT
135336396Ssklower	/* This happens if other end sent a DR when  the user was waiting
135436396Ssklower	 * on a receive.
135536396Ssklower	 * Processing the DR includes putting us in REFWAIT state.
135636396Ssklower	 */
135736396Ssklower	{
135836396Ssklower		if(trick_hc)
135936396Ssklower		return ECONNABORTED;
136036396Ssklower	}
136136396Ssklower;
136236396Ssklower
136336396Ssklower/* TP0 only */
136436396SsklowerTP_REFWAIT		<==		[ TP_OPEN, TP_CRSENT, TP_LISTENING ] 	T_NETRESET
136536396Ssklower	( $P.tp_class != TP_CLASS_4 )
136636396Ssklower		/* 0 or (4 and 0) */
136736396Ssklower		/* in OPEN class will be 0 or 4 but not both */
136836396Ssklower		/* in CRSENT or LISTENING it could be in negotiation, hence both */
136936396Ssklower		/* Actually, this shouldn't ever happen in LISTENING */
137036396Ssklower	{
137136396Ssklower		ASSERT( $P.tp_state != TP_LISTENING );
137236396Ssklower		tp_indicate(T_DISCONNECT, $P, ECONNRESET);
137336396Ssklower		tp_soisdisconnected($P);
137436396Ssklower	}
137536396Ssklower;
137636396Ssklower
137736396Ssklower/* TP4: ignore resets */
137836396SsklowerSAME		<==		[ TP_OPEN, TP_CRSENT, TP_AKWAIT,
137936396Ssklower						TP_CLOSING, TP_LISTENING ] 				T_NETRESET
138036396Ssklower	DEFAULT
138136396Ssklower	NULLACTION
138236396Ssklower;
138336396Ssklower
138436396Ssklower/* applicable in TP4, TP0 */
138536396SsklowerSAME			<==		[ TP_CLOSED, TP_REFWAIT ]				T_NETRESET
138636396Ssklower	DEFAULT
138736396Ssklower	NULLACTION
138836396Ssklower;
138936396Ssklower
139036396Ssklower/* C'EST TOUT */
1391