xref: /csrg-svn/sys/netiso/tp.trans (revision 48739)
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*48739Ssklower/*	@(#)tp.trans	7.7 (Berkeley) 04/26/91 */
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);
31836396Ssklower			$$.e_data = MNULL;
31936396Ssklower		}
32038841Ssklower	}
32138841Ssklower;
32238841Ssklower
32338841SsklowerTP_OPEN		<==		 TP_CONFIRMING  								T_ACPT_req
32438841Ssklower	( $P.tp_class == TP_CLASS_0 )
32538841Ssklower	{
32638841Ssklower		IncStat(ts_tp0_conn);
32738841Ssklower		IFTRACE(D_CONN)
32838841Ssklower			tptrace(TPPTmisc, "Confiming", $P, 0,0,0);
32938841Ssklower		ENDTRACE
33038841Ssklower		IFDEBUG(D_CONN)
33138841Ssklower			printf("Confirming connection: $P" );
33238841Ssklower		ENDDEBUG
33338841Ssklower		soisconnected($P.tp_sock);
33438841Ssklower		(void) tp_emit(CC_TPDU_type, $P, 0,0, MNULL) ;
33538841Ssklower		$P.tp_fcredit = 1;
33638841Ssklower	}
33738841Ssklower;
33838841Ssklower
33938841SsklowerTP_AKWAIT		<==		 TP_CONFIRMING  							T_ACPT_req
34038841Ssklower	(tp_emit(CC_TPDU_type, $P, 0,0, MCPY($P.tp_ucddata, M_NOWAIT)) == 0)
34138841Ssklower	{
34238841Ssklower		IncStat(ts_tp4_conn); /* even though not quite open */
34338841Ssklower		IFTRACE(D_CONN)
34438841Ssklower			tptrace(TPPTmisc, "Confiming", $P, 0,0,0);
34538841Ssklower		ENDTRACE
34638841Ssklower		IFDEBUG(D_CONN)
34738841Ssklower			printf("Confirming connection: $P" );
34838841Ssklower		ENDDEBUG
34938841Ssklower		soisconnecting($P.tp_sock);
35047281Ssklower		if (($P.tp_rx_strat & TPRX_FASTSTART) && ($P.tp_fcredit > 0))
35138841Ssklower			$P.tp_cong_win = $P.tp_fcredit;
35236396Ssklower		$P.tp_retrans = $P.tp_Nretrans;
35336396Ssklower		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_cc_ticks);
35438841Ssklower	}
35536396Ssklower;
35636396Ssklower
35736396Ssklower/* TP4 only */
35838841SsklowerTP_CLOSED		<==		 TP_CONFIRMING								T_ACPT_req
35936396Ssklower	DEFAULT /* emit failed */
36036396Ssklower	{
36136396Ssklower		register struct tp_ref *r = $P.tp_refp;
36236396Ssklower
36336396Ssklower		IFDEBUG(D_CONN)
36436396Ssklower			printf("event: CR_TPDU emit CC failed done " );
36536396Ssklower		ENDDEBUG
36638841Ssklower		soisdisconnected($P.tp_sock);
36736396Ssklower		tp_recycle_tsuffix( $P );
36836396Ssklower		tp_freeref(r);
36936396Ssklower		tp_detach($P);
37036396Ssklower	}
37136396Ssklower;
37236396Ssklower
37336396Ssklower/* applicable in TP4, TP0 */
37436396SsklowerTP_CRSENT		<==		TP_CLOSED								T_CONN_req
37536396Ssklower	DEFAULT
37636396Ssklower	{
37736396Ssklower		int error;
37836396Ssklower		struct mbuf *data = MNULL;
37936396Ssklower
38036396Ssklower		IFTRACE(D_CONN)
38137469Ssklower			tptrace(TPPTmisc, "T_CONN_req flags ucddata", (int)$P.tp_flags,
38237469Ssklower			$P.tp_ucddata, 0, 0);
38336396Ssklower		ENDTRACE
38437469Ssklower		data =  MCPY($P.tp_ucddata, M_WAIT);
38537469Ssklower		if (data) {
38636396Ssklower			IFDEBUG(D_CONN)
38736396Ssklower				printf("T_CONN_req.trans m_copy cc 0x%x\n",
38837469Ssklower					$P.tp_ucddata);
38937469Ssklower				dump_mbuf(data, "sosnd @ T_CONN_req");
39036396Ssklower			ENDDEBUG
39136396Ssklower		}
39236396Ssklower
39336396Ssklower		if (error = tp_emit(CR_TPDU_type, $P, 0, 0, data) )
39436396Ssklower			return error; /* driver WON'T change state; will return error */
39536396Ssklower
39636396Ssklower		$P.tp_refp->tpr_state = REF_OPEN; /* has timers */
39736396Ssklower		if($P.tp_class != TP_CLASS_0) {
39836396Ssklower			$P.tp_retrans = $P.tp_Nretrans;
39936396Ssklower			tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_cr_ticks);
40036396Ssklower		}
40136396Ssklower	}
40236396Ssklower;
40336396Ssklower
40436396Ssklower/* applicable in TP4, TP0, but state TP_AKWAIT doesn't apply to TP0 */
40536396SsklowerTP_REFWAIT 		<==		[ TP_CRSENT, TP_AKWAIT, TP_OPEN ] 			DR_TPDU
40636396Ssklower	DEFAULT
40736396Ssklower	{
408*48739Ssklower		sbflush(&$P.tp_Xrcv); /* purge non-delivered data data */
409*48739Ssklower		if ($$.e_datalen > 0) {
41036396Ssklower			sbappendrecord(&$P.tp_Xrcv, $$.e_data);
41136396Ssklower			$$.e_data = MNULL;
41236396Ssklower		}
413*48739Ssklower		tp_indicate(T_DISCONNECT, $P, 0);
41436396Ssklower		tp_soisdisconnected($P);
41536396Ssklower		if ($P.tp_class != TP_CLASS_0) {
41636396Ssklower			if ($P.tp_state == TP_OPEN ) {
41736396Ssklower				tp_euntimeout($P.tp_refp, TM_data_retrans); /* all */
41836396Ssklower				tp_cuntimeout($P.tp_refp, TM_retrans);
41936396Ssklower				tp_cuntimeout($P.tp_refp, TM_inact);
42036396Ssklower				tp_cuntimeout($P.tp_refp, TM_sendack);
42136396Ssklower			}
42236396Ssklower			tp_cuntimeout($P.tp_refp, TM_retrans);
42336396Ssklower			if( $$.e_sref !=  0 )
42436396Ssklower				(void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL);
42536396Ssklower		}
42636396Ssklower	}
42736396Ssklower;
42836396Ssklower
42936396SsklowerSAME 			<==		TP_CLOSED 									DR_TPDU
43036396Ssklower	DEFAULT
43136396Ssklower	{
43236396Ssklower		if( $$.e_sref != 0 )
43336396Ssklower			(void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL);
43436396Ssklower		/* reference timer already set - reset it to be safe (???) */
43536396Ssklower		tp_euntimeout($P.tp_refp, TM_reference); /* all */
43636396Ssklower		tp_etimeout($P.tp_refp, TM_reference, 0, 0, 0, (int)$P.tp_refer_ticks);
43736396Ssklower	}
43836396Ssklower;
43936396Ssklower
44036396Ssklower/* NBS(34) */
44136396SsklowerTP_REFWAIT 		<==  	TP_CRSENT  									ER_TPDU
44236396Ssklower	DEFAULT
44336396Ssklower	{
44436396Ssklower		tp_cuntimeout($P.tp_refp, TM_retrans);
445*48739Ssklower		tp_indicate(ER_TPDU, $P, $$.e_reason);
44636396Ssklower		tp_soisdisconnected($P);
44736396Ssklower	}
44836396Ssklower;
44936396Ssklower
45036396Ssklower/* NBS(27) */
45136396SsklowerTP_REFWAIT		<==		TP_CLOSING									DR_TPDU
45236396Ssklower	DEFAULT
45336396Ssklower	{
45436396Ssklower		tp_cuntimeout($P.tp_refp, TM_retrans);
45536396Ssklower		tp_soisdisconnected($P);
45636396Ssklower	}
45736396Ssklower;
45836396Ssklower/* these two transitions are the same but can't be combined because xebec
45936396Ssklower * can't handle the use of $$.e_reason if they're combined
46036396Ssklower */
46136396Ssklower/* NBS(27) */
46236396SsklowerTP_REFWAIT		<==		TP_CLOSING									ER_TPDU
46336396Ssklower	DEFAULT
46436396Ssklower	{
465*48739Ssklower		tp_indicate(ER_TPDU, $P, $$.e_reason);
46636396Ssklower		tp_cuntimeout($P.tp_refp, TM_retrans);
46736396Ssklower		tp_soisdisconnected($P);
46836396Ssklower	}
46936396Ssklower;
47036396Ssklower/* NBS(27) */
47136396SsklowerTP_REFWAIT		<==		TP_CLOSING									DC_TPDU
47236396Ssklower	DEFAULT
47336396Ssklower	{
47436396Ssklower		tp_cuntimeout($P.tp_refp, TM_retrans);
47536396Ssklower		tp_soisdisconnected($P);
47636396Ssklower	}
47736396Ssklower;
47836396Ssklower
47936396Ssklower/* NBS(21) */
48036396SsklowerSAME 			<== 	TP_CLOSED 						[ CC_TPDU, CR_TPDU ]
48136396Ssklower	DEFAULT
48236396Ssklower	{	/* don't ask me why we have to do this - spec says so */
48336396Ssklower		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_NO_SESSION, MNULL);
48436396Ssklower		/* don't bother with retransmissions of the DR */
48536396Ssklower	}
48636396Ssklower;
48736396Ssklower
48836396Ssklower/* NBS(34) */
48936396SsklowerTP_REFWAIT 		<== 	TP_OPEN  				 					ER_TPDU
49036396Ssklower	($P.tp_class == TP_CLASS_0)
49136396Ssklower	{
49236396Ssklower		tp_soisdisconnecting($P.tp_sock);
493*48739Ssklower		tp_indicate(ER_TPDU, $P, $$.e_reason);
49436396Ssklower		tp_soisdisconnected($P);
49536396Ssklower		tp_netcmd( $P, CONN_CLOSE );
49636396Ssklower	}
49736396Ssklower;
49836396Ssklower
49936396SsklowerTP_CLOSING 		<== 	[ TP_AKWAIT, TP_OPEN ]  					ER_TPDU
50036396Ssklower	DEFAULT
50136396Ssklower	{
50236396Ssklower		if ($P.tp_state == TP_OPEN) {
50336396Ssklower			tp_euntimeout($P.tp_refp, TM_data_retrans); /* all */
50436396Ssklower			tp_cuntimeout($P.tp_refp, TM_inact);
50536396Ssklower			tp_cuntimeout($P.tp_refp, TM_sendack);
50636396Ssklower		}
50736396Ssklower		tp_soisdisconnecting($P.tp_sock);
508*48739Ssklower		tp_indicate(ER_TPDU, $P, $$.e_reason);
50936396Ssklower		$P.tp_retrans = $P.tp_Nretrans;
51036396Ssklower		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks);
51136396Ssklower		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_PROTO_ERR, MNULL);
51236396Ssklower	}
51336396Ssklower;
51436396Ssklower/* NBS(6) */
51536396SsklowerTP_OPEN			<==		TP_CRSENT									CC_TPDU
51636396Ssklower	($P.tp_class == TP_CLASS_0)
51736396Ssklower	{
51836396Ssklower		tp_cuntimeout($P.tp_refp, TM_retrans);
51936396Ssklower		IncStat(ts_tp0_conn);
52036396Ssklower		$P.tp_fcredit = 1;
52136396Ssklower		soisconnected($P.tp_sock);
52236396Ssklower	}
52336396Ssklower;
52436396Ssklower
52536396SsklowerTP_OPEN			<==		TP_CRSENT									CC_TPDU
52636396Ssklower	DEFAULT
52736396Ssklower	{
52836396Ssklower		IFDEBUG(D_CONN)
52936396Ssklower			printf("trans: CC_TPDU in CRSENT state flags 0x%x\n",
53036396Ssklower				(int)$P.tp_flags);
53136396Ssklower		ENDDEBUG
53236396Ssklower		IncStat(ts_tp4_conn);
53336396Ssklower		$P.tp_fref = $$.e_sref;
53436396Ssklower		$P.tp_fcredit = $$.e_cdt;
53539921Ssklower		$P.tp_ackrcvd = 0;
53647281Ssklower		if (($P.tp_rx_strat & TPRX_FASTSTART) && ($$.e_cdt > 0))
53736396Ssklower			$P.tp_cong_win = $$.e_cdt;
53836396Ssklower		tp_getoptions($P);
53936396Ssklower		tp_cuntimeout($P.tp_refp, TM_retrans);
54037469Ssklower		if ($P.tp_ucddata) {
54136396Ssklower			IFDEBUG(D_CONN)
54237469Ssklower				printf("dropping user connect data cc 0x%x\n",
54337469Ssklower					$P.tp_ucddata->m_len);
54436396Ssklower			ENDDEBUG
54537469Ssklower			m_freem($P.tp_ucddata);
54637469Ssklower			$P.tp_ucddata = 0;
54736396Ssklower		}
54836396Ssklower		soisconnected($P.tp_sock);
54936396Ssklower		if ($$.e_datalen > 0) {
55036396Ssklower			ASSERT($P.tp_Xrcv.sb_cc == 0); /* should be empty */
55136396Ssklower			sbappendrecord(&$P.tp_Xrcv, $$.e_data);
55236396Ssklower			$$.e_data = MNULL;
55336396Ssklower		}
55436396Ssklower
55536396Ssklower		(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
55636396Ssklower		tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
55736396Ssklower	}
55836396Ssklower;
55936396Ssklower
56036396Ssklower/* TP4 only */
56136396SsklowerSAME			<==		TP_CRSENT									TM_retrans
56236396Ssklower	(	$P.tp_retrans > 0 )
56336396Ssklower	{
56436396Ssklower		struct mbuf *data = MNULL;
56536396Ssklower		int error;
56636396Ssklower
56736396Ssklower		IncStat(ts_retrans_cr);
56839921Ssklower		$P.tp_cong_win = 1;
56939921Ssklower		$P.tp_ackrcvd = 0;
57037469Ssklower		data = MCPY($P.tp_ucddata, M_NOWAIT);
57137469Ssklower		if($P.tp_ucddata) {
57236396Ssklower			IFDEBUG(D_CONN)
57337469Ssklower				printf("TM_retrans.trans m_copy cc 0x%x\n", data);
57437469Ssklower				dump_mbuf($P.tp_ucddata, "sosnd @ TM_retrans");
57536396Ssklower			ENDDEBUG
57636396Ssklower			if( data == MNULL )
57736396Ssklower				return ENOBUFS;
57836396Ssklower		}
57936396Ssklower
58036396Ssklower		$P.tp_retrans --;
58136396Ssklower		if( error = tp_emit(CR_TPDU_type, $P, 0, 0, data) ) {
58236396Ssklower			$P.tp_sock->so_error = error;
58336396Ssklower		}
58436396Ssklower		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_cr_ticks);
58536396Ssklower	}
58636396Ssklower;
58736396Ssklower
58836396Ssklower/* TP4 only  */
58936396SsklowerTP_REFWAIT		<==		TP_CRSENT									TM_retrans
59036396Ssklower	DEFAULT /* no more CR retransmissions */
59136396Ssklower	{
59236396Ssklower		IncStat(ts_conn_gaveup);
59336396Ssklower		$P.tp_sock->so_error = ETIMEDOUT;
59436396Ssklower		tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
59536396Ssklower		tp_soisdisconnected($P);
59636396Ssklower	}
59736396Ssklower;
59836396Ssklower
59936396Ssklower/* TP4 only */
60036396SsklowerSAME 			<==	 TP_AKWAIT											CR_TPDU
60136396Ssklower	DEFAULT
60236396Ssklower	/* duplicate CR (which doesn't really exist in the context of
60336396Ssklower	 * a connectionless network layer)
60436396Ssklower	 * Doesn't occur in class 0.
60536396Ssklower	 */
60636396Ssklower	{
60736396Ssklower		int error;
60837469Ssklower		struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
60936396Ssklower
61037469Ssklower		if( error = tp_emit(CC_TPDU_type, $P, 0, 0, data) ) {
61136396Ssklower			$P.tp_sock->so_error = error;
61236396Ssklower		}
61336396Ssklower		$P.tp_retrans = $P.tp_Nretrans;
61436396Ssklower		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_cc_ticks);
61536396Ssklower	}
61636396Ssklower;
61736396Ssklower
61836396Ssklower/* TP4 only */
61936396SsklowerTP_OPEN			<==		TP_AKWAIT 										DT_TPDU
62036396Ssklower	( IN_RWINDOW( $P, $$.e_seq,
62136396Ssklower					$P.tp_rcvnxt, SEQ($P, $P.tp_rcvnxt + $P.tp_lcredit)) )
62236396Ssklower	{
62336396Ssklower		int doack;
62436396Ssklower
62537469Ssklower		/*
62637469Ssklower		 * Get rid of any confirm or connect data, so that if we
62737469Ssklower		 * crash or close, it isn't thought of as disconnect data.
62837469Ssklower		 */
62937469Ssklower		if ($P.tp_ucddata) {
63037469Ssklower			m_freem($P.tp_ucddata);
63137469Ssklower			$P.tp_ucddata = 0;
63236396Ssklower		}
63336396Ssklower		tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
63436396Ssklower		tp_cuntimeout($P.tp_refp, TM_retrans);
63536396Ssklower		soisconnected($P.tp_sock);
63636396Ssklower		tp_getoptions($P);
63736396Ssklower		tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
63836396Ssklower
63936396Ssklower		/* see also next 2 transitions, if you make any changes */
64036396Ssklower
64136396Ssklower		doack = tp_stash($P, $E);
64236396Ssklower		IFDEBUG(D_DATA)
64336396Ssklower			printf("tp_stash returns %d\n",doack);
64436396Ssklower		ENDDEBUG
64536396Ssklower
64636396Ssklower		if(doack) {
64736396Ssklower			(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL );
64836396Ssklower			tp_ctimeout($P.tp_refp, TM_sendack, (int)$P.tp_keepalive_ticks);
64936396Ssklower		} else
65036396Ssklower			tp_ctimeout( $P.tp_refp, TM_sendack, (int)$P.tp_sendack_ticks);
65136396Ssklower
65236396Ssklower		IFDEBUG(D_DATA)
65336396Ssklower			printf("after stash calling sbwakeup\n");
65436396Ssklower		ENDDEBUG
65536396Ssklower	}
65636396Ssklower;
65736396Ssklower
65836396SsklowerSAME			<==		TP_OPEN 									DT_TPDU
65936396Ssklower	( $P.tp_class == TP_CLASS_0 )
66036396Ssklower	{
66136396Ssklower		tp0_stash($P, $E);
66236396Ssklower		sbwakeup( &$P.tp_sock->so_rcv );
66336396Ssklower
66436396Ssklower		IFDEBUG(D_DATA)
66536396Ssklower			printf("after stash calling sbwakeup\n");
66636396Ssklower		ENDDEBUG
66736396Ssklower	}
66836396Ssklower;
66936396Ssklower
67036396Ssklower/* TP4 only */
67136396SsklowerSAME			<==		TP_OPEN 									DT_TPDU
67236396Ssklower	( IN_RWINDOW( $P, $$.e_seq,
67336396Ssklower					$P.tp_rcvnxt, SEQ($P, $P.tp_rcvnxt + $P.tp_lcredit)) )
67436396Ssklower	{
67536396Ssklower		int doack; /* tells if we must ack immediately */
67636396Ssklower
67736396Ssklower		tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
67836396Ssklower		sbwakeup( &$P.tp_sock->so_rcv );
67936396Ssklower
68036396Ssklower		doack = tp_stash($P, $E);
68136396Ssklower		IFDEBUG(D_DATA)
68236396Ssklower			printf("tp_stash returns %d\n",doack);
68336396Ssklower		ENDDEBUG
68436396Ssklower
68536396Ssklower		if(doack)
68636396Ssklower			(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL );
68736396Ssklower		else
68836396Ssklower			tp_ctimeout_MIN( $P.tp_refp, TM_sendack, (int)$P.tp_sendack_ticks);
68936396Ssklower
69036396Ssklower		IFDEBUG(D_DATA)
69136396Ssklower			printf("after stash calling sbwakeup\n");
69236396Ssklower		ENDDEBUG
69336396Ssklower	}
69436396Ssklower;
69536396Ssklower
69636396Ssklower/* Not in window  - we must ack under certain circumstances, namely
69736396Ssklower * a) if the seq number is below lwe but > lwe - (max credit ever given)
69836396Ssklower * (to handle lost acks) Can use max-possible-credit for this ^^^.
69936396Ssklower * and
70036396Ssklower * b) seq number is > uwe but < uwe + previously sent & withdrawn credit
70136396Ssklower *
70236396Ssklower * (see 12.2.3.8.1 of ISO spec, p. 73)
70336396Ssklower * We just always ack.
70436396Ssklower */
70536396Ssklower/* TP4 only */
70636396SsklowerSAME 			<== 	[ TP_OPEN, TP_AKWAIT ]							DT_TPDU
70736396Ssklower	DEFAULT /* Not in window */
70836396Ssklower	{
70936396Ssklower		IFTRACE(D_DATA)
71036396Ssklower			tptrace(TPPTmisc, "NIW seq rcvnxt lcredit ",
71136396Ssklower				$$.e_seq, $P.tp_rcvnxt, $P.tp_lcredit, 0);
71236396Ssklower		ENDTRACE
71336396Ssklower		IncStat(ts_dt_niw);
71436396Ssklower		m_freem($$.e_data);
71536396Ssklower		tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
71636396Ssklower		(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL );
71736396Ssklower	}
71836396Ssklower;
71936396Ssklower
72036396Ssklower/* TP4 only */
72136396SsklowerTP_OPEN			<==		TP_AKWAIT										AK_TPDU
72236396Ssklower	DEFAULT
72336396Ssklower	{
72437469Ssklower		if ($P.tp_ucddata) {
72537469Ssklower			m_freem($P.tp_ucddata);
72637469Ssklower			$P.tp_ucddata = 0;
72736396Ssklower		}
72836396Ssklower		(void) tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq);
72936396Ssklower		tp_cuntimeout($P.tp_refp, TM_retrans);
73036396Ssklower
73136396Ssklower		tp_getoptions($P);
73236396Ssklower		soisconnected($P.tp_sock);
73336396Ssklower		IFTRACE(D_CONN)
73436396Ssklower			struct socket *so = $P.tp_sock;
73536396Ssklower			tptrace(TPPTmisc,
73636396Ssklower			"called sosiconn: so so_state rcv.sb_sel rcv.sb_flags",
73736396Ssklower				so, so->so_state, so->so_rcv.sb_sel, so->so_rcv.sb_flags);
73836396Ssklower			tptrace(TPPTmisc,
73936396Ssklower			"called sosiconn 2: so_qlen so_error so_rcv.sb_cc so_head",
74036396Ssklower				so->so_qlen, so->so_error, so->so_rcv.sb_cc, so->so_head);
74136396Ssklower		ENDTRACE
74236396Ssklower
74336396Ssklower		tp_ctimeout($P.tp_refp, TM_sendack, (int)$P.tp_keepalive_ticks);
74436396Ssklower		tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
74536396Ssklower	}
74636396Ssklower;
74736396Ssklower
74836396Ssklower/* TP4 only */
74936396SsklowerTP_OPEN 		<== 	[ TP_OPEN, TP_AKWAIT ]						XPD_TPDU
75047281Ssklower	($P.tp_Xrcvnxt == $$.e_seq)
75136396Ssklower	{
75236396Ssklower		if( $P.tp_state == TP_AKWAIT ) {
75337469Ssklower			if ($P.tp_ucddata) {
75437469Ssklower				m_freem($P.tp_ucddata);
75537469Ssklower				$P.tp_ucddata = 0;
75636396Ssklower			}
75736396Ssklower			tp_cuntimeout($P.tp_refp, TM_retrans);
75836396Ssklower			tp_getoptions($P);
75936396Ssklower			soisconnected($P.tp_sock);
76036396Ssklower			tp_ctimeout($P.tp_refp, TM_sendack, (int)$P.tp_keepalive_ticks);
76136396Ssklower			tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
76236396Ssklower		}
76336396Ssklower		IFTRACE(D_XPD)
76436396Ssklower		tptrace(TPPTmisc, "XPD tpdu accepted Xrcvnxt, e_seq datalen m_len\n",
76536396Ssklower				$P.tp_Xrcvnxt,$$.e_seq,  $$.e_datalen, $$.e_data->m_len);
76636396Ssklower		ENDTRACE
76736396Ssklower
76837469Ssklower		$P.tp_sock->so_state |= SS_RCVATMARK;
76947281Ssklower		$$.e_data->m_flags |= M_EOR;
77037469Ssklower		sbinsertoob(&$P.tp_Xrcv, $$.e_data);
77136396Ssklower		IFDEBUG(D_XPD)
77236396Ssklower			dump_mbuf($$.e_data, "XPD TPDU: tp_Xrcv");
77336396Ssklower		ENDDEBUG
77436396Ssklower		tp_indicate(T_XDATA, $P, 0);
77536396Ssklower		sbwakeup( &$P.tp_Xrcv );
77636396Ssklower
77736396Ssklower		(void) tp_emit(XAK_TPDU_type, $P, $P.tp_Xrcvnxt, 0, MNULL);
77836396Ssklower		SEQ_INC($P, $P.tp_Xrcvnxt);
77936396Ssklower	}
78036396Ssklower;
78136396Ssklower
78236396Ssklower/* TP4 only */
78336396SsklowerSAME			<==		TP_OPEN 									T_USR_Xrcvd
78436396Ssklower	DEFAULT
78536396Ssklower	{
78636396Ssklower		if( $P.tp_Xrcv.sb_cc == 0 ) {
78736396Ssklower			/* kludge for select(): */
78837469Ssklower			/* $P.tp_sock->so_state &= ~SS_OOBAVAIL; */
78936396Ssklower		}
79036396Ssklower	}
79136396Ssklower	/* OLD WAY:
79236396Ssklower	 * Ack only after the user receives the XPD.  This is better for
79336396Ssklower	 * users that use one XPD right after another.
79436396Ssklower	 * Acking right away (the NEW WAY, see the prev. transition) is
79536396Ssklower	 * better for occasional * XPD, when the receiving user doesn't
79636396Ssklower	 * want to read the XPD immediately (which is session's behavior).
79736396Ssklower	 *
79836396Ssklower		int error = tp_emit(XAK_TPDU_type, $P, $P.tp_Xrcvnxt, 0, MNULL);
79936396Ssklower		SEQ_INC($P, $P.tp_Xrcvnxt);
80036396Ssklower		return error;
80136396Ssklower	*/
80236396Ssklower;
80336396Ssklower
80436396Ssklower/* NOTE: presently if the user doesn't read the connection data
80536396Ssklower * before and expedited data PDU comes in, the connection data will
80636396Ssklower * be dropped. This is a bug.  To avoid it, we need somewhere else
80736396Ssklower * to put the connection data.
80836396Ssklower * On the other hand, we need not to have it sitting around forever.
80936396Ssklower * This is a problem with the idea of trying to accommodate
81036396Ssklower * data on connect w/ a passive-open user interface.
81136396Ssklower */
81236396Ssklower/* TP4 only */
81336396Ssklower
81436396SsklowerSAME	 		<== 	[ TP_AKWAIT, TP_OPEN ] 							XPD_TPDU
81536396Ssklower	DEFAULT /* not in window or cdt==0 */
81636396Ssklower	{
81736396Ssklower		IFTRACE(D_XPD)
81836396Ssklower			tptrace(TPPTmisc, "XPD tpdu niw (Xrcvnxt, e_seq) or not cdt (cc)\n",
81936396Ssklower				$P.tp_Xrcvnxt, $$.e_seq,  $P.tp_Xrcv.sb_cc , 0);
82036396Ssklower		ENDTRACE
82136396Ssklower		if( $P.tp_Xrcvnxt != $$.e_seq )
82236396Ssklower			IncStat(ts_xpd_niw);
82336396Ssklower		if( $P.tp_Xrcv.sb_cc ) {
82436396Ssklower			/* might as well kick 'em again */
82536396Ssklower			tp_indicate(T_XDATA, $P, 0);
82636396Ssklower			IncStat(ts_xpd_dup);
82736396Ssklower		}
82836396Ssklower		m_freem($$.e_data);
82936396Ssklower		tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
83036396Ssklower		/* don't send an xack because the xak gives "last one received", not
83136396Ssklower		 * "next one i expect" (dumb)
83236396Ssklower		 */
83336396Ssklower	}
83436396Ssklower;
83536396Ssklower
83636396Ssklower/* Occurs (AKWAIT, OPEN) when parent (listening) socket gets aborted, and tries
83736396Ssklower * to detach all its "children"
83836396Ssklower * Also (CRSENT) when user kills a job that's doing a connect()
83936396Ssklower */
84036396SsklowerTP_REFWAIT		<== 	TP_CRSENT 										T_DETACH
84136396Ssklower	($P.tp_class == TP_CLASS_0)
84236396Ssklower	{
84336396Ssklower		struct socket *so = $P.tp_sock;
84436396Ssklower
84536396Ssklower		/* detach from parent socket so it can finish closing */
84636396Ssklower		if (so->so_head) {
84736396Ssklower			if (!soqremque(so, 0) && !soqremque(so, 1))
84836396Ssklower				panic("tp: T_DETACH");
84936396Ssklower			so->so_head = 0;
85036396Ssklower		}
85136396Ssklower		tp_soisdisconnecting($P.tp_sock);
85236396Ssklower		tp_netcmd( $P, CONN_CLOSE);
85336396Ssklower		tp_soisdisconnected($P);
85436396Ssklower	}
85536396Ssklower;
85636396Ssklower
85736396Ssklower/* TP4 only */
85838841SsklowerTP_CLOSING		<== [ TP_CLOSING, TP_AKWAIT, TP_CRSENT, TP_CONFIRMING ]	T_DETACH
85936396Ssklower	DEFAULT
86036396Ssklower	{
86136396Ssklower		struct socket *so = $P.tp_sock;
86237469Ssklower		struct mbuf *data = MNULL;
86336396Ssklower
86436396Ssklower		/* detach from parent socket so it can finish closing */
86536396Ssklower		if (so->so_head) {
86636396Ssklower			if (!soqremque(so, 0) && !soqremque(so, 1))
86736396Ssklower				panic("tp: T_DETACH");
86836396Ssklower			so->so_head = 0;
86936396Ssklower		}
87036396Ssklower		if ($P.tp_state != TP_CLOSING) {
87136396Ssklower			tp_soisdisconnecting($P.tp_sock);
87237469Ssklower			data = MCPY($P.tp_ucddata, M_NOWAIT);
87337469Ssklower			(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_NORMAL_DISC, data);
87436396Ssklower			$P.tp_retrans = $P.tp_Nretrans;
87536396Ssklower			tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks);
87636396Ssklower		}
87736396Ssklower	}
87836396Ssklower;
87936396Ssklower
88036396SsklowerTP_REFWAIT		<==		[ TP_OPEN, TP_CRSENT ]		 	  			T_DISC_req
88136396Ssklower	( $P.tp_class == TP_CLASS_0 )
88236396Ssklower	{
88336396Ssklower		tp_soisdisconnecting($P.tp_sock);
88436396Ssklower		tp_netcmd( $P, CONN_CLOSE);
88536396Ssklower		tp_soisdisconnected($P);
88636396Ssklower	}
88736396Ssklower;
88836396Ssklower
88936396Ssklower/* TP4 only */
89038841SsklowerTP_CLOSING		<==	[ TP_AKWAIT, TP_OPEN, TP_CRSENT, TP_CONFIRMING ]  T_DISC_req
89136396Ssklower	DEFAULT
89236396Ssklower	{
89337469Ssklower		struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
89436396Ssklower
89536396Ssklower		if($P.tp_state == TP_OPEN) {
89636396Ssklower			tp_euntimeout($P.tp_refp, TM_data_retrans); /* all */
89736396Ssklower			tp_cuntimeout($P.tp_refp, TM_inact);
89836396Ssklower			tp_cuntimeout($P.tp_refp, TM_sendack);
89936396Ssklower		}
90037469Ssklower		if (data) {
90136396Ssklower			IFDEBUG(D_CONN)
90237469Ssklower				printf("T_DISC_req.trans tp_ucddata 0x%x\n",
90337469Ssklower					$P.tp_ucddata);
90437469Ssklower				dump_mbuf(data, "ucddata @ T_DISC_req");
90536396Ssklower			ENDDEBUG
90636396Ssklower		}
90736396Ssklower		tp_soisdisconnecting($P.tp_sock);
90836396Ssklower		$P.tp_retrans = $P.tp_Nretrans;
90936396Ssklower		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks);
91036396Ssklower
91136396Ssklower		if( trick_hc )
91236396Ssklower			return tp_emit(DR_TPDU_type, $P, 0, $$.e_reason, data);
91336396Ssklower	}
91436396Ssklower;
91536396Ssklower
91636396Ssklower/* TP4 only */
91736396SsklowerSAME			<==		TP_AKWAIT									TM_retrans
91836396Ssklower	( $P.tp_retrans > 0 )
91936396Ssklower	{
92036396Ssklower		int error;
92137469Ssklower		struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
92236396Ssklower
92336396Ssklower		IncStat(ts_retrans_cc);
92436396Ssklower		$P.tp_retrans --;
92539921Ssklower		$P.tp_cong_win = 1;
92639921Ssklower		$P.tp_ackrcvd = 0;
92739921Ssklower
92837469Ssklower		if( error = tp_emit(CC_TPDU_type, $P, 0, 0, data) )
92936396Ssklower			$P.tp_sock->so_error = error;
93036396Ssklower		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_cc_ticks);
93136396Ssklower	}
93236396Ssklower;
93336396Ssklower
93436396Ssklower/* TP4 only */
93536396SsklowerTP_CLOSING		<==		TP_AKWAIT									TM_retrans
93636396Ssklower	DEFAULT  /* out of time */
93736396Ssklower	{
93836396Ssklower		IncStat(ts_conn_gaveup);
93936396Ssklower		tp_soisdisconnecting($P.tp_sock);
94036396Ssklower		$P.tp_sock->so_error = ETIMEDOUT;
94136396Ssklower		tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
94236396Ssklower		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_CONGEST, MNULL);
94336396Ssklower		$P.tp_retrans = $P.tp_Nretrans;
94436396Ssklower		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks);
94536396Ssklower	}
94636396Ssklower;
94736396Ssklower
94836396Ssklower/* the retrans timers had better go off BEFORE the inactivity timer does,
94936396Ssklower * if transmissions are going on.
95036396Ssklower * (i.e., TM_inact should be greater than timer for all retrans plus ack
95136396Ssklower * turnaround)
95236396Ssklower */
95336396Ssklower/* TP4 only */
95436396SsklowerTP_CLOSING 		<==		TP_OPEN		   [ TM_inact, TM_retrans, TM_data_retrans ]
95536396Ssklower	DEFAULT
95636396Ssklower	{
95736396Ssklower		tp_euntimeout($P.tp_refp, TM_data_retrans); /* all */
95836396Ssklower		tp_cuntimeout($P.tp_refp, TM_inact);
95936396Ssklower		tp_cuntimeout($P.tp_refp, TM_sendack);
96036396Ssklower
96136396Ssklower		IncStat(ts_conn_gaveup);
96236396Ssklower		tp_soisdisconnecting($P.tp_sock);
96336396Ssklower		$P.tp_sock->so_error = ETIMEDOUT;
96436396Ssklower		tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
96536396Ssklower		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_CONGEST_2, MNULL);
96636396Ssklower		$P.tp_retrans = $P.tp_Nretrans;
96736396Ssklower		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks);
96836396Ssklower	}
96936396Ssklower;
97036396Ssklower
97136396Ssklower/* TP4 only */
97236396SsklowerSAME			<==		TP_OPEN										TM_retrans
97336396Ssklower	( $P.tp_retrans > 0 )
97436396Ssklower	{
97539921Ssklower		$P.tp_cong_win = 1;
97639921Ssklower		$P.tp_ackrcvd = 0;
97736396Ssklower		/* resume XPD */
97836396Ssklower		if	( $P.tp_Xsnd.sb_mb )  {
97937469Ssklower			struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc);
98036396Ssklower			/* m_copy doesn't preserve the m_xlink field, but at this pt.
98136396Ssklower			 * that doesn't matter
98236396Ssklower			 */
98336396Ssklower
98436396Ssklower			IFTRACE(D_XPD)
98536396Ssklower				tptrace(TPPTmisc, "XPD retrans: Xuna Xsndnxt sndhiwat snduna",
98636396Ssklower					$P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndhiwat,
98736396Ssklower					$P.tp_snduna);
98836396Ssklower			ENDTRACE
98936396Ssklower			IFDEBUG(D_XPD)
99036396Ssklower				dump_mbuf(m, "XPD retrans emitting M");
99136396Ssklower			ENDDEBUG
99236396Ssklower			IncStat(ts_retrans_xpd);
99336396Ssklower			$P.tp_retrans --;
99436396Ssklower			(void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
99536396Ssklower			tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_xpd_ticks);
99636396Ssklower		}
99736396Ssklower	}
99836396Ssklower;
99936396Ssklower
100036396Ssklower/* TP4 only */
100136396SsklowerSAME 			<==		TP_OPEN									TM_data_retrans
100236396Ssklower	( $$.e_retrans > 0 )
100336396Ssklower	{
100436396Ssklower		register 	SeqNum			low, lowsave = 0;
100536396Ssklower		register	struct tp_rtc 	*r = $P.tp_snduna_rtc;
100636396Ssklower		register	struct mbuf 	*m;
100736396Ssklower		register	SeqNum			high = $$.e_high;
100836396Ssklower
100939921Ssklower		low = $P.tp_snduna;
101039921Ssklower		lowsave = high = low;
101139921Ssklower
101239921Ssklower		tp_euntimeout_lss($P.tp_refp, TM_data_retrans,
101339921Ssklower			SEQ_ADD($P, $P.tp_sndhiwat, 1));
101439921Ssklower		$P.tp_retrans_hiwat = $P.tp_sndhiwat;
101539921Ssklower
101636396Ssklower		if (($P.tp_rx_strat & TPRX_EACH) == 0)
101736396Ssklower			high = (high>low)?low:high;
101836396Ssklower
101936396Ssklower		if( $P.tp_rx_strat & TPRX_USE_CW ) {
102036396Ssklower			register int i;
102136396Ssklower
102236396Ssklower			$P.tp_cong_win = 1;
102339921Ssklower			$P.tp_ackrcvd = 0;
102439921Ssklower			i = SEQ_ADD($P, low, $P.tp_cong_win);
102536396Ssklower
102639921Ssklower			high = SEQ_MIN($P, high, $P.tp_sndhiwat);
102739921Ssklower
102836396Ssklower		}
102936396Ssklower
103036396Ssklower		while( SEQ_LEQ($P, low, high) ){
103136396Ssklower			if ( r == (struct tp_rtc *)0 ){
103236396Ssklower				IFDEBUG(D_RTC)
103336396Ssklower					printf( "tp: retrans rtc list is GONE!\n");
103436396Ssklower				ENDDEBUG
103536396Ssklower				break;
103636396Ssklower			}
103736396Ssklower			if ( r->tprt_seq == low ){
103836396Ssklower				if(( m = m_copy(r->tprt_data, 0, r->tprt_octets ))== MNULL)
103936396Ssklower					break;
104036396Ssklower				(void) tp_emit(DT_TPDU_type, $P, low, r->tprt_eot, m);
104136396Ssklower				IncStat(ts_retrans_dt);
104236396Ssklower				SEQ_INC($P, low );
104336396Ssklower			}
104436396Ssklower			r = r->tprt_next;
104536396Ssklower		}
104639921Ssklower/* CE_BIT
104736396Ssklower		if ( SEQ_LEQ($P, lowsave, high) ){
104839921Ssklower*/
104936396Ssklower			$$.e_retrans --;
105036396Ssklower			tp_etimeout($P.tp_refp, TM_data_retrans, (caddr_t)lowsave,
105136396Ssklower					(caddr_t)high, $$.e_retrans,
105236396Ssklower					($P.tp_Nretrans - $$.e_retrans) * (int)$P.tp_dt_ticks);
105339921Ssklower/* CE_BIT
105436396Ssklower		}
105539921Ssklower*/
105636396Ssklower	}
105736396Ssklower;
105836396Ssklower
105936396Ssklower/* TP4 only */
106036396SsklowerSAME	 		<==		TP_CLOSING									TM_retrans
106136396Ssklower	(	$P.tp_retrans > 0 )
106236396Ssklower	{
106336396Ssklower		$P.tp_retrans --;
106436396Ssklower		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_DR_NO_REAS, MNULL);
106536396Ssklower		IncStat(ts_retrans_dr);
106636396Ssklower		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks);
106736396Ssklower	}
106836396Ssklower;
106936396Ssklower
107036396Ssklower/* TP4 only */
107136396SsklowerTP_REFWAIT 		<==		TP_CLOSING									TM_retrans
107236396Ssklower	DEFAULT	/* no more retrans - gave up */
107336396Ssklower	{
107436396Ssklower		$P.tp_sock->so_error = ETIMEDOUT;
107536396Ssklower		$P.tp_refp->tpr_state = REF_FROZEN;
107636396Ssklower		tp_recycle_tsuffix( $P );
107736396Ssklower		tp_etimeout($P.tp_refp, TM_reference, 0,0,0, (int)$P.tp_refer_ticks);
107836396Ssklower	}
107936396Ssklower;
108036396Ssklower
108136396Ssklower/*
108236396Ssklower * The resources are kept around until the ref timer goes off.
108336396Ssklower * The suffices are wiped out sooner so they can be reused right away.
108436396Ssklower */
108536396Ssklower/* applicable in TP4, TP0 */
108636396SsklowerTP_CLOSED 		<==		TP_REFWAIT 									TM_reference
108736396Ssklower	DEFAULT
108836396Ssklower	{
108936396Ssklower		tp_freeref($P.tp_refp);
109036396Ssklower		tp_detach($P);
109136396Ssklower	}
109236396Ssklower;
109336396Ssklower
109436396Ssklower/* applicable in TP4, TP0 */
109536396Ssklower/* A duplicate CR from connectionless network layer can't happen */
109636396SsklowerSAME 			<== 	TP_OPEN 							[ CR_TPDU, CC_TPDU ]
109736396Ssklower	DEFAULT
109836396Ssklower	{
109936396Ssklower		if( $P.tp_class != TP_CLASS_0) {
110036396Ssklower			tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
110136396Ssklower			if ( $E.ev_number == CC_TPDU )
110236396Ssklower				(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
110336396Ssklower		}
110436396Ssklower		/* ignore it if class 0 - state tables are blank for this */
110536396Ssklower	}
110636396Ssklower;
110736396Ssklower
110836396Ssklower/* applicable in TP4, TP0 */
110936396SsklowerSAME			<== 	TP_OPEN									T_DATA_req
111036396Ssklower	DEFAULT
111136396Ssklower	{
111236396Ssklower		IFTRACE(D_DATA)
111336396Ssklower			tptrace(TPPTmisc, "T_DATA_req sndhiwat snduna fcredit, tpcb",
111436396Ssklower				$P.tp_sndhiwat, $P.tp_snduna, $P.tp_fcredit, $P);
111536396Ssklower		ENDTRACE
111636396Ssklower
111736396Ssklower		tp_send($P);
111836396Ssklower	}
111936396Ssklower;
112036396Ssklower
112136396Ssklower/* TP4 only */
112236396SsklowerSAME			<==		TP_OPEN										T_XPD_req
112336396Ssklower	DEFAULT
112436396Ssklower		/* T_XPD_req was issued by sosend iff xpd socket buf was empty
112536396Ssklower		 * at time of sosend(),
112636396Ssklower		 * AND (which means) there were no unacknowledged XPD tpdus outstanding!
112736396Ssklower		 */
112836396Ssklower	{
112936396Ssklower		int error = 0;
113036396Ssklower
113136396Ssklower		/* resume XPD */
113236396Ssklower		if	( $P.tp_Xsnd.sb_mb )  {
113337469Ssklower			struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc);
113436396Ssklower			/* m_copy doesn't preserve the m_xlink field, but at this pt.
113536396Ssklower			 * that doesn't matter
113636396Ssklower			 */
113736396Ssklower
113836396Ssklower			IFTRACE(D_XPD)
113936396Ssklower				tptrace(TPPTmisc, "XPD req: Xuna Xsndnxt sndhiwat snduna",
114036396Ssklower					$P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndhiwat,
114136396Ssklower					$P.tp_snduna);
114236396Ssklower			ENDTRACE
114336396Ssklower			IFDEBUG(D_XPD)
114436396Ssklower				printf("T_XPD_req: sb_cc 0x%x\n", $P.tp_Xsnd.sb_cc);
114536396Ssklower				dump_mbuf(m, "XPD req emitting M");
114636396Ssklower			ENDDEBUG
114736396Ssklower			error =
114836396Ssklower				tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
114936396Ssklower			$P.tp_retrans = $P.tp_Nretrans;
115036396Ssklower			tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_xpd_ticks);
115136396Ssklower			SEQ_INC($P, $P.tp_Xsndnxt);
115236396Ssklower		}
115336396Ssklower		if(trick_hc)
115436396Ssklower			return error;
115536396Ssklower	}
115636396Ssklower;
115736396Ssklower
115836396Ssklower/* TP4, faked ack in TP0 when cons send completes */
115936396SsklowerSAME 			<==		TP_OPEN 									AK_TPDU
116036396Ssklower	( tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq)  )
116136396Ssklower
116236396Ssklower	/* tp_goodack == true means
116336396Ssklower	 * EITHER it actually acked something heretofore unacknowledged
116436396Ssklower	 * OR no news but the credit should be processed.
116536396Ssklower	 */
116636396Ssklower	{
116736396Ssklower		IFDEBUG(D_ACKRECV)
116836396Ssklower			printf("GOOD ACK seq 0x%x cdt 0x%x\n", $$.e_seq, $$.e_cdt);
116936396Ssklower		ENDDEBUG
117036396Ssklower		if( $P.tp_class != TP_CLASS_0) {
117136396Ssklower			tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
117236396Ssklower			tp_euntimeout_lss($P.tp_refp, TM_data_retrans, $$.e_seq);
117336396Ssklower		}
117436396Ssklower		sbwakeup( &$P.tp_sock->so_snd );
117536396Ssklower
117639921Ssklower		if ($P.tp_sndhiwat <= $P.tp_retrans_hiwat &&
117739921Ssklower			$P.tp_snduna <= $P.tp_retrans_hiwat) {
117839921Ssklower
117939921Ssklower			register    struct mbuf     *m;
118039921Ssklower			/* extern      struct mbuf     *m_copy(); */
118139921Ssklower			register    struct tp_rtc   *r;
118239921Ssklower			SeqNum      high, retrans, low_save;
118339921Ssklower
118439921Ssklower			high = SEQ_MIN($P, SEQ_ADD($P, $P.tp_snduna,
118539921Ssklower					MIN($P.tp_cong_win, $P.tp_fcredit)) - 1,
118639921Ssklower					$P.tp_sndhiwat);
118739921Ssklower			low_save = retrans = SEQ_MAX($P, SEQ_ADD($P, $P.tp_last_retrans, 1),
118839921Ssklower					$P.tp_snduna);
118939921Ssklower			for (; SEQ_LEQ($P, retrans, high); SEQ_INC($P, retrans)) {
119039921Ssklower
119139921Ssklower				for (r = $P.tp_snduna_rtc; r; r = r->tprt_next){
119239921Ssklower					if ( r->tprt_seq == retrans ){
119339921Ssklower						if(( m = m_copy(r->tprt_data, 0, r->tprt_octets ))
119439921Ssklower								== MNULL)
119539921Ssklower							break;
119639921Ssklower						(void) tp_emit(DT_TPDU_type, $P, retrans,
119739921Ssklower							r->tprt_eot, m);
119839921Ssklower						$P.tp_last_retrans = retrans;
119939921Ssklower						IncStat(ts_retrans_dt);
120039921Ssklower						break;
120139921Ssklower					}
120239921Ssklower				}
120339921Ssklower				if ( r == (struct tp_rtc *)0 ){
120439921Ssklower					IFDEBUG(D_RTC)
120539921Ssklower						printf( "tp: retrans rtc list is GONE!\n");
120639921Ssklower					ENDDEBUG
120739921Ssklower					break;
120839921Ssklower				}
120939921Ssklower			}
121039921Ssklower			tp_etimeout($P.tp_refp, TM_data_retrans, (caddr_t)low_save,
121139921Ssklower					(caddr_t)high, $P.tp_retrans, (int)$P.tp_dt_ticks);
121239921Ssklower			if (SEQ_DEC($P, retrans) == $P.tp_retrans_hiwat)
121339921Ssklower				tp_send($P);
121439921Ssklower		}
121539921Ssklower		else {
121639921Ssklower			tp_send($P);
121739921Ssklower		}
121836396Ssklower		IFDEBUG(D_ACKRECV)
121936396Ssklower			printf("GOOD ACK new sndhiwat 0x%x\n", $P.tp_sndhiwat);
122036396Ssklower		ENDDEBUG
122136396Ssklower	}
122236396Ssklower;
122336396Ssklower
122436396Ssklower/* TP4, and TP0 after sending a CC or possibly a CR */
122536396SsklowerSAME			<==		TP_OPEN 			 						 AK_TPDU
122636396Ssklower	DEFAULT
122736396Ssklower	{
122836396Ssklower		IFTRACE(D_ACKRECV)
122936396Ssklower			tptrace(TPPTmisc, "BOGUS ACK fcc_present, tp_r_subseq e_subseq",
123036396Ssklower				$$.e_fcc_present, $P.tp_r_subseq, $$.e_subseq, 0);
123136396Ssklower		ENDTRACE
123236396Ssklower		if( $P.tp_class != TP_CLASS_0 ) {
123336396Ssklower
123436396Ssklower			if ( !$$.e_fcc_present ) {
123536396Ssklower				/* send ACK with FCC */
123636396Ssklower				IncStat( ts_ackreason[_ACK_FCC_] );
123736396Ssklower				(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 1, MNULL);
123836396Ssklower			}
123936396Ssklower			tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
124036396Ssklower		}
124136396Ssklower	}
124236396Ssklower;
124336396Ssklower
124436396Ssklower/* NBS(47) */
124536396Ssklower	/* goes in at *** */
124636396Ssklower		/* just so happens that this is never true now, because we allow
124736396Ssklower		 * only 1 packet in the queue at once (this could be changed)
124836396Ssklower		if	( $P.tp_Xsnd.sb_mb )  {
124936396Ssklower			struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, ??);
125036396Ssklower
125136396Ssklower			(void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
125236396Ssklower			$P.tp_retrans = $P.tp_Nretrans;
125336396Ssklower			tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_xpd_ticks);
125436396Ssklower			SEQ_INC($P, $P.tp_Xsndnxt);
125536396Ssklower		}
125636396Ssklower		 */
125736396Ssklower	/* end of the above hack */
125836396Ssklower
125936396Ssklower/* TP4 only */
126042944SsklowerSAME			<== 	TP_OPEN										XAK_TPDU
126136396Ssklower	( tp_goodXack($P, $$.e_seq) )
126236396Ssklower	/* tp_goodXack checks for good ack, removes the correct
126336396Ssklower	 * tpdu from the queue and  returns 1 if ack was legit, 0 if not.
126436396Ssklower	 * also updates tp_Xuna
126536396Ssklower	 */
126636396Ssklower	{
126736396Ssklower		tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
126836396Ssklower		tp_cuntimeout($P.tp_refp, TM_retrans);
126936396Ssklower
127036396Ssklower		sbwakeup( &$P.tp_sock->so_snd );
127136396Ssklower
127236396Ssklower		/* resume normal data */
127336396Ssklower		tp_send($P);
127436396Ssklower	}
127536396Ssklower;
127636396Ssklower
127742944Ssklower/* TP4, and TP0 after sending a CC or possibly a CR */
127842944SsklowerSAME			<==		TP_OPEN 			 						XAK_TPDU
127942944Ssklower	DEFAULT
128042944Ssklower	{
128142944Ssklower		IFTRACE(D_ACKRECV)
128242944Ssklower			tptrace(TPPTmisc, "BOGUS XACK eventtype ", $E.ev_number, 0, 0,0);
128342944Ssklower		ENDTRACE
128442944Ssklower		if( $P.tp_class != TP_CLASS_0 ) {
128542944Ssklower			tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
128642944Ssklower		}
128742944Ssklower	}
128842944Ssklower;
128942944Ssklower
129036396Ssklower/* TP4 only */
129136396SsklowerSAME			<==		TP_OPEN 								TM_sendack
129236396Ssklower	DEFAULT
129336396Ssklower	{
129436396Ssklower		IFTRACE(D_TIMER)
129536396Ssklower			tptrace(TPPTsendack, -1, $P.tp_lcredit, $P.tp_sent_uwe,
129636396Ssklower			$P.tp_sent_lcdt, 0);
129736396Ssklower		ENDTRACE
129836396Ssklower		IncPStat($P, tps_n_TMsendack);
129936396Ssklower		(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
130036396Ssklower	}
130136396Ssklower;
130236396Ssklower
130336396Ssklower/* TP0 only */
130436396SsklowerSAME			<==		TP_OPEN 									T_USR_rcvd
130536396Ssklower	($P.tp_class == TP_CLASS_0)
130647281Ssklower	{
130747281Ssklower		if (sbspace(&$P.tp_sock->so_rcv) > 0)
130847281Ssklower			tp0_openflow($P);
130947281Ssklower	}
131036396Ssklower;
131136396Ssklower
131236396Ssklower/* TP4 only */
131336396Ssklower		/* If old credit was zero,
131436396Ssklower		 * we'd better inform other side that we now have space
131536396Ssklower		 * But this is not enough.  Sender might not yet have
131636396Ssklower		 * seen an ack with cdt 0 but it might still think the
131736396Ssklower		 * window is closed, so it's going to wait.
131836396Ssklower		 * Best to send an ack each time.
131936396Ssklower		 * Strictly speaking, this ought to be a function of the
132036396Ssklower		 * general ack strategy.
132136396Ssklower		 */
132236396SsklowerSAME			<==		TP_OPEN 									T_USR_rcvd
132336396Ssklower	DEFAULT
132436396Ssklower	{
132536396Ssklower		if( trick_hc ) {
132636396Ssklower			IncStat(ts_ackreason[_ACK_USRRCV_]);
132739921Ssklower
132839921Ssklower			/* send an ACK only if there's new information */
132939921Ssklower			LOCAL_CREDIT( $P );
133039921Ssklower			if (($P.tp_rcvnxt != $P.tp_sent_rcvnxt) ||
133139921Ssklower				($P.tp_lcredit != $P.tp_sent_lcdt))
133239921Ssklower
133339921Ssklower				return tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
133436396Ssklower		}
133536396Ssklower	}
133636396Ssklower;
133736396Ssklower
133836396Ssklower/* applicable in TP4, TP0 */
133936396SsklowerSAME			<==		TP_REFWAIT 				[ T_USR_rcvd, T_USR_Xrcvd ]
134036396Ssklower	DEFAULT
134136396Ssklower	/* This happens if other end sent a DR when  the user was waiting
134236396Ssklower	 * on a receive.
134336396Ssklower	 * Processing the DR includes putting us in REFWAIT state.
134436396Ssklower	 */
134536396Ssklower	{
134636396Ssklower		if(trick_hc)
134736396Ssklower		return ECONNABORTED;
134836396Ssklower	}
134936396Ssklower;
135036396Ssklower
135136396Ssklower/* TP0 only */
135236396SsklowerTP_REFWAIT		<==		[ TP_OPEN, TP_CRSENT, TP_LISTENING ] 	T_NETRESET
135336396Ssklower	( $P.tp_class != TP_CLASS_4 )
135436396Ssklower		/* 0 or (4 and 0) */
135536396Ssklower		/* in OPEN class will be 0 or 4 but not both */
135636396Ssklower		/* in CRSENT or LISTENING it could be in negotiation, hence both */
135736396Ssklower		/* Actually, this shouldn't ever happen in LISTENING */
135836396Ssklower	{
135936396Ssklower		ASSERT( $P.tp_state != TP_LISTENING );
136036396Ssklower		tp_indicate(T_DISCONNECT, $P, ECONNRESET);
136136396Ssklower		tp_soisdisconnected($P);
136236396Ssklower	}
136336396Ssklower;
136436396Ssklower
136536396Ssklower/* TP4: ignore resets */
136636396SsklowerSAME		<==		[ TP_OPEN, TP_CRSENT, TP_AKWAIT,
136736396Ssklower						TP_CLOSING, TP_LISTENING ] 				T_NETRESET
136836396Ssklower	DEFAULT
136936396Ssklower	NULLACTION
137036396Ssklower;
137136396Ssklower
137236396Ssklower/* applicable in TP4, TP0 */
137336396SsklowerSAME			<==		[ TP_CLOSED, TP_REFWAIT ]				T_NETRESET
137436396Ssklower	DEFAULT
137536396Ssklower	NULLACTION
137636396Ssklower;
137736396Ssklower
137836396Ssklower/* C'EST TOUT */
1379