xref: /csrg-svn/sys/netiso/tp.trans (revision 51208)
150849Ssklower/* NEW */
249268Sbostic/*-
349268Sbostic * Copyright (c) 1991 The Regents of the University of California.
449268Sbostic * All rights reserved.
549268Sbostic *
649268Sbostic * %sccs.include.redist.c%
749268Sbostic *
8*51208Ssklower *	@(#)tp.trans	7.15 (Berkeley) 09/30/91
949268Sbostic */
1049268Sbostic
1136396Ssklower/***********************************************************
1236396Ssklower		Copyright IBM Corporation 1987
1336396Ssklower
1436396Ssklower                      All Rights Reserved
1536396Ssklower
1636396SsklowerPermission to use, copy, modify, and distribute this software and its
1736396Ssklowerdocumentation for any purpose and without fee is hereby granted,
1836396Ssklowerprovided that the above copyright notice appear in all copies and that
1936396Ssklowerboth that copyright notice and this permission notice appear in
2036396Ssklowersupporting documentation, and that the name of IBM not be
2136396Ssklowerused in advertising or publicity pertaining to distribution of the
2236396Ssklowersoftware without specific, written prior permission.
2336396Ssklower
2436396SsklowerIBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
2536396SsklowerALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
2636396SsklowerIBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
2736396SsklowerANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
2836396SsklowerWHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
2936396SsklowerARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
3036396SsklowerSOFTWARE.
3136396Ssklower
3236396Ssklower******************************************************************/
3336396Ssklower
3436396Ssklower/*
3536396Ssklower * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
3636396Ssklower */
3736396Ssklower/* $Header: tp.trans,v 5.1 88/10/12 12:22:07 root Exp $
3836396Ssklower *
3936396Ssklower * Transition file for TP.
4036396Ssklower *
4136396Ssklower * DO NOT:
4236396Ssklower * - change the order of any of the events or states.  to do so will
4336396Ssklower *   make tppt, netstat, etc. cease working.
4436396Ssklower *
4536396Ssklower * NOTE:
4636396Ssklower * some hooks exist for data on (dis)connect, but it's ***NOT***SUPPORTED***
4736396Ssklower * (read: may not work!)
4836396Ssklower *
4936396Ssklower * I tried to put everything that causes a change of state in here, hence
5036396Ssklower * there are some seemingly trivial events  like T_DETACH and T_LISTEN_req.
5136396Ssklower *
5236396Ssklower * Almost everything having to do w/ setting & cancelling timers is here
5336396Ssklower * but once it was debugged, I moved the setting of the
5436396Ssklower * keepalive (sendack) timer to tp_emit(), where an AK_TPDU is sent.
5536396Ssklower * This is so the code wouldn't be duplicated all over creation in here.
5636396Ssklower *
5736396Ssklower */
5836396Ssklower*PROTOCOL tp
5936396Ssklower
6036396Ssklower*INCLUDE
6136396Ssklower{
62*51208Ssklower/* @(#)tp.trans	7.15 (Berkeley) 09/30/91 */
6336396Ssklower#include "param.h"
6436396Ssklower#include "socket.h"
6536396Ssklower#include "socketvar.h"
6636396Ssklower#include "protosw.h"
6736396Ssklower#include "mbuf.h"
6836396Ssklower#include "time.h"
6936396Ssklower#include "errno.h"
7036396Ssklower#include "../netiso/tp_param.h"
7136396Ssklower#include "../netiso/tp_stat.h"
7236396Ssklower#include "../netiso/tp_pcb.h"
7336396Ssklower#include "../netiso/tp_tpdu.h"
7436396Ssklower#include "../netiso/argo_debug.h"
7536396Ssklower#include "../netiso/tp_trace.h"
7636396Ssklower#include "../netiso/iso_errno.h"
7736396Ssklower#include "../netiso/tp_seq.h"
7836396Ssklower#include "../netiso/cons.h"
7936396Ssklower
8036396Ssklower#define DRIVERTRACE TPPTdriver
8137469Ssklower#define sbwakeup(sb)	sowakeup(p->tp_sock, sb);
8237469Ssklower#define MCPY(d, w) (d ? m_copym(d, 0, (int)M_COPYALL, w): 0)
8336396Ssklower
8436396Ssklowerstatic 	trick_hc = 1;
8536396Ssklower
8636396Ssklowerint 	tp_emit(),
8736396Ssklower		tp_goodack(),				tp_goodXack(),
8836396Ssklower		tp_stash()
8936396Ssklower;
9036396Ssklowervoid	tp_indicate(),				tp_getoptions(),
9136396Ssklower		tp_soisdisconnecting(), 	tp_soisdisconnected(),
9236396Ssklower		tp_recycle_tsuffix(),
9336396Ssklower		tp_etimeout(),				tp_euntimeout(),
9451204Ssklower		tp_ctimeout(),
9536396Ssklower		tp_cuntimeout(),			tp_ctimeout_MIN(),
9636396Ssklower		tp_freeref(),				tp_detach(),
9736396Ssklower		tp0_stash(), 				tp0_send(),
9836396Ssklower		tp_netcmd(),				tp_send()
9936396Ssklower;
10036396Ssklower
10136396Ssklowertypedef  struct tp_pcb tpcb_struct;
10236396Ssklower
10336396Ssklower
10436396Ssklower}
10536396Ssklower
10636396Ssklower*PCB    tpcb_struct 	SYNONYM  P
10736396Ssklower
10836396Ssklower*STATES
10936396Ssklower
11036396SsklowerTP_CLOSED
11136396SsklowerTP_CRSENT
11236396SsklowerTP_AKWAIT
11336396SsklowerTP_OPEN
11436396SsklowerTP_CLOSING
11536396SsklowerTP_REFWAIT
11636396SsklowerTP_LISTENING	/* Local to this implementation */
11738841SsklowerTP_CONFIRMING	/* Local to this implementation */
11836396Ssklower
11936396Ssklower*EVENTS		{ struct timeval e_time; } 		SYNONYM  E
12036396Ssklower
12136396Ssklower /*
12236396Ssklower  * C (typically cancelled) timers  -
12336396Ssklower  *
12436396Ssklower  * let these be the first ones so for the sake of convenience
12536396Ssklower  * their values are 0--> n-1
12636396Ssklower  * DO NOT CHANGE THE ORDER OF THESE TIMER EVENTS!!
12736396Ssklower  */
12836396Ssklower TM_inact
12936396Ssklower TM_retrans
13036396Ssklower				/* TM_retrans is used for all
13136396Ssklower				 * simple retransmissions - CR,CC,XPD,DR
13236396Ssklower				 */
13336396Ssklower
13436396Ssklower TM_sendack
13551204Ssklower				/* TM_sendack does dual duty - keepalive AND closed-window
13651204Ssklower				 * Probes.
13736396Ssklower				 * It's set w/ keepalive-ticks every time an ack is sent.
13836396Ssklower				 * (this is done in (void) tp_emit() ).
13951204Ssklower				 * Whenever a DT arrives which doesn't require immediate acking,
14051204Ssklower				 * a separate fast-timeout flag is set ensuring 200ms response.
14136396Ssklower				 */
14236396Ssklower TM_notused
14336396Ssklower
14436396Ssklower /*
14536396Ssklower  * E (typically expired) timers - these may be in any order.
14636396Ssklower  * These cause procedures to be executed directly; may not
14736396Ssklower  * cause an 'event' as we know them here.
14836396Ssklower  */
14936396Ssklower TM_reference		{ SeqNum e_low; SeqNum e_high; int e_retrans; }
15036396Ssklower TM_data_retrans	{ SeqNum e_low; SeqNum e_high; int e_retrans; }
15136396Ssklower
15236396Ssklower/* NOTE: in tp_input is a minor optimization that assumes that
15336396Ssklower * for all tpdu types that can take e_data and e_datalen, these
15436396Ssklower * fields fall in the same place in the event structure, that is,
15536396Ssklower * e_data is the first field and e_datalen is the 2nd field.
15636396Ssklower */
15736396Ssklower
15836396Ssklower ER_TPDU  	 	{
15936396Ssklower				  u_char		e_reason;
16036396Ssklower				}
16136396Ssklower CR_TPDU  	 	{ struct mbuf 	*e_data;	/* first field */
16236396Ssklower				  int 			e_datalen; /* 2nd field */
16336396Ssklower				  u_int			e_cdt;
16436396Ssklower				}
16536396Ssklower DR_TPDU   	 	{ struct mbuf 	*e_data;	/* first field */
16636396Ssklower				  int 			e_datalen; /* 2nd field */
16736396Ssklower				  u_short		e_sref;
16836396Ssklower				  u_char		e_reason;
16936396Ssklower				}
17036396Ssklower DC_TPDU
17136396Ssklower CC_TPDU   	 	{ struct mbuf 	*e_data;	/* first field */
17236396Ssklower				  int 			e_datalen; /* 2nd field */
17336396Ssklower				  u_short		e_sref;
17436396Ssklower				  u_int			e_cdt;
17536396Ssklower				}
17636396Ssklower AK_TPDU		{ u_int			e_cdt;
17736396Ssklower				  SeqNum 	 	e_seq;
17836396Ssklower				  SeqNum 	 	e_subseq;
17936396Ssklower				  u_char 	 	e_fcc_present;
18036396Ssklower				}
18136396Ssklower DT_TPDU		{ struct mbuf	*e_data; 	/* first field */
18236396Ssklower				  int 			e_datalen; /* 2nd field */
18336396Ssklower				  u_int 		e_eot;
18436396Ssklower				  SeqNum		e_seq;
18536396Ssklower				}
18636396Ssklower XPD_TPDU		{ struct mbuf 	*e_data;	/* first field */
18736396Ssklower				  int 			e_datalen; 	/* 2nd field */
18836396Ssklower				  SeqNum 		e_seq;
18936396Ssklower				}
19036396Ssklower XAK_TPDU		{ SeqNum 		e_seq;		}
19136396Ssklower
19236396Ssklower T_CONN_req
19336396Ssklower T_DISC_req		{ u_char		e_reason; 	}
19436396Ssklower T_LISTEN_req
19536396Ssklower T_DATA_req
19636396Ssklower T_XPD_req
19736396Ssklower T_USR_rcvd
19836396Ssklower T_USR_Xrcvd
19936396Ssklower T_DETACH
20036396Ssklower T_NETRESET
20138841Ssklower T_ACPT_req
20236396Ssklower
20336396Ssklower
20436396Ssklower*TRANSITIONS
20536396Ssklower
20636396Ssklower
20736396Ssklower/* TP_AKWAIT doesn't exist in TP 0 */
20836396SsklowerSAME			<==			TP_AKWAIT			[ CC_TPDU, DC_TPDU, XAK_TPDU ]
20936396Ssklower	DEFAULT
21036396Ssklower	NULLACTION
21136396Ssklower;
21236396Ssklower
21336396Ssklower
21436396Ssklower/* applicable in TP4, TP0 */
21536396SsklowerSAME			<==			TP_REFWAIT								DR_TPDU
21636396Ssklower	( $$.e_sref !=  0 )
21736396Ssklower	{
21836396Ssklower		(void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL);
21936396Ssklower	}
22036396Ssklower;
22136396Ssklower
22236396Ssklower/* applicable in TP4, TP0 */
22336396SsklowerSAME			<==			TP_REFWAIT			[ CR_TPDU, CC_TPDU, DT_TPDU,
22436396Ssklower					DR_TPDU, XPD_TPDU, AK_TPDU, XAK_TPDU, DC_TPDU, ER_TPDU ]
22536396Ssklower	DEFAULT
22636396Ssklower	{
22736396Ssklower#		ifdef TP_DEBUG
22836396Ssklower		if( $E.ev_number != AK_TPDU )
22936396Ssklower			printf("TPDU 0x%x in REFWAIT!!!!\n", $E.ev_number);
23036396Ssklower#		endif TP_DEBUG
23136396Ssklower	}
23236396Ssklower;
23336396Ssklower
23436396Ssklower/* applicable in TP4, TP0 */
23536396SsklowerSAME			<==			TP_REFWAIT				[ T_DETACH, T_DISC_req ]
23636396Ssklower	DEFAULT
23736396Ssklower	NULLACTION
23836396Ssklower;
23936396Ssklower
24036396Ssklower/* applicable in TP4, TP0 */
24136396SsklowerSAME			<==			TP_CRSENT								 AK_TPDU
24236396Ssklower	($P.tp_class == TP_CLASS_0)
24336396Ssklower	{
24436396Ssklower		/* oh, man is this grotesque or what? */
24536396Ssklower		(void) tp_goodack($P, $$.e_cdt, $$.e_seq,  $$.e_subseq);
24636396Ssklower		/* but it's necessary because this pseudo-ack may happen
24736396Ssklower		 * before the CC arrives, but we HAVE to adjust the
24836396Ssklower		 * snduna as a result of the ack, WHENEVER it arrives
24936396Ssklower		 */
25036396Ssklower	}
25136396Ssklower;
25236396Ssklower
25336396Ssklower/* applicable in TP4, TP0 */
25436396SsklowerSAME			<==			TP_CRSENT
25536396Ssklower					[ CR_TPDU, DC_TPDU, DT_TPDU, XPD_TPDU,  XAK_TPDU ]
25636396Ssklower	DEFAULT
25736396Ssklower	NULLACTION
25836396Ssklower;
25936396Ssklower
26036396Ssklower/* applicable in TP4, TP0 */
26136396SsklowerSAME			<==			TP_CLOSED					[ DT_TPDU, XPD_TPDU,
26236396Ssklower										ER_TPDU, DC_TPDU, AK_TPDU, XAK_TPDU ]
26336396Ssklower	DEFAULT
26436396Ssklower	NULLACTION
26536396Ssklower;
26636396Ssklower
26736396Ssklower/* TP_CLOSING doesn't exist in TP 0 */
26836396SsklowerSAME 			<== 		TP_CLOSING
26936396Ssklower					[ CC_TPDU, CR_TPDU, DT_TPDU, XPD_TPDU, AK_TPDU, XAK_TPDU ]
27036396Ssklower	DEFAULT
27136396Ssklower	NULLACTION
27236396Ssklower;
27336396Ssklower
27436396Ssklower
27536396Ssklower/* DC_TPDU doesn't exist in TP 0 */
27636396SsklowerSAME			<==			TP_OPEN						  DC_TPDU
27736396Ssklower	DEFAULT
27836396Ssklower	NULLACTION
27936396Ssklower;
28036396Ssklower
28136396Ssklower/* applicable in TP4, TP0 */
28236396SsklowerSAME			<==		 	TP_LISTENING  [DR_TPDU, CC_TPDU, DT_TPDU, XPD_TPDU,
28336396Ssklower										 ER_TPDU, DC_TPDU, AK_TPDU, XAK_TPDU ]
28436396Ssklower	DEFAULT
28536396Ssklower	NULLACTION
28636396Ssklower;
28736396Ssklower
28836396Ssklower/* applicable in TP4, TP0 */
28936396SsklowerTP_LISTENING	<==			TP_CLOSED  							T_LISTEN_req
29036396Ssklower	DEFAULT
29136396Ssklower	NULLACTION
29236396Ssklower;
29336396Ssklower
29436396Ssklower/* applicable in TP4, TP0 */
29536396SsklowerTP_CLOSED  		<== 		[ TP_LISTENING, TP_CLOSED ] 			T_DETACH
29636396Ssklower	DEFAULT
29736396Ssklower	{
29836396Ssklower		tp_detach($P);
29936396Ssklower	}
30036396Ssklower;
30136396Ssklower
30238841SsklowerTP_CONFIRMING	<==		 TP_LISTENING  								CR_TPDU
30338841Ssklower	( $P.tp_class == TP_CLASS_0)
30436396Ssklower	{
30551204Ssklower		$P.tp_refstate = REF_OPEN; /* has timers ??? */
30636396Ssklower	}
30736396Ssklower;
30836396Ssklower
30938841SsklowerTP_CONFIRMING		<==		 TP_LISTENING  							CR_TPDU
31038841Ssklower	DEFAULT
31136396Ssklower	{
31236396Ssklower		IFTRACE(D_CONN)
31336396Ssklower			tptrace(TPPTmisc, "CR datalen data", $$.e_datalen, $$.e_data,0,0);
31436396Ssklower		ENDTRACE
31536396Ssklower		IFDEBUG(D_CONN)
31636396Ssklower			printf("CR datalen 0x%x data 0x%x", $$.e_datalen, $$.e_data);
31736396Ssklower		ENDDEBUG
31851204Ssklower		$P.tp_refstate = REF_OPEN; /* has timers */
31938841Ssklower		$P.tp_fcredit = $$.e_cdt;
32036396Ssklower
32136396Ssklower		if ($$.e_datalen > 0) {
32236396Ssklower			/* n/a for class 0 */
32336396Ssklower			ASSERT($P.tp_Xrcv.sb_cc == 0);
32436396Ssklower			sbappendrecord(&$P.tp_Xrcv, $$.e_data);
32536396Ssklower			$$.e_data = MNULL;
32636396Ssklower		}
32738841Ssklower	}
32838841Ssklower;
32938841Ssklower
33038841SsklowerTP_OPEN		<==		 TP_CONFIRMING  								T_ACPT_req
33138841Ssklower	( $P.tp_class == TP_CLASS_0 )
33238841Ssklower	{
33338841Ssklower		IncStat(ts_tp0_conn);
33438841Ssklower		IFTRACE(D_CONN)
33538841Ssklower			tptrace(TPPTmisc, "Confiming", $P, 0,0,0);
33638841Ssklower		ENDTRACE
33738841Ssklower		IFDEBUG(D_CONN)
33838841Ssklower			printf("Confirming connection: $P" );
33938841Ssklower		ENDDEBUG
34038841Ssklower		soisconnected($P.tp_sock);
34138841Ssklower		(void) tp_emit(CC_TPDU_type, $P, 0,0, MNULL) ;
34238841Ssklower		$P.tp_fcredit = 1;
34338841Ssklower	}
34438841Ssklower;
34538841Ssklower
34638841SsklowerTP_AKWAIT		<==		 TP_CONFIRMING  							T_ACPT_req
34738841Ssklower	(tp_emit(CC_TPDU_type, $P, 0,0, MCPY($P.tp_ucddata, M_NOWAIT)) == 0)
34838841Ssklower	{
34938841Ssklower		IncStat(ts_tp4_conn); /* even though not quite open */
35038841Ssklower		IFTRACE(D_CONN)
35138841Ssklower			tptrace(TPPTmisc, "Confiming", $P, 0,0,0);
35238841Ssklower		ENDTRACE
35338841Ssklower		IFDEBUG(D_CONN)
35438841Ssklower			printf("Confirming connection: $P" );
35538841Ssklower		ENDDEBUG
35650973Ssklower		tp_getoptions($P);
35738841Ssklower		soisconnecting($P.tp_sock);
35847281Ssklower		if (($P.tp_rx_strat & TPRX_FASTSTART) && ($P.tp_fcredit > 0))
35951204Ssklower			$P.tp_cong_win = $P.tp_fcredit * $P.tp_l_tpdusize;
36036396Ssklower		$P.tp_retrans = $P.tp_Nretrans;
36151204Ssklower		tp_ctimeout($P, TM_retrans, (int)$P.tp_cc_ticks);
36238841Ssklower	}
36336396Ssklower;
36436396Ssklower
36536396Ssklower/* TP4 only */
36638841SsklowerTP_CLOSED		<==		 TP_CONFIRMING								T_ACPT_req
36736396Ssklower	DEFAULT /* emit failed */
36836396Ssklower	{
36936396Ssklower		register struct tp_ref *r = $P.tp_refp;
37036396Ssklower
37136396Ssklower		IFDEBUG(D_CONN)
37236396Ssklower			printf("event: CR_TPDU emit CC failed done " );
37336396Ssklower		ENDDEBUG
37438841Ssklower		soisdisconnected($P.tp_sock);
37536396Ssklower		tp_recycle_tsuffix( $P );
37636396Ssklower		tp_freeref(r);
37736396Ssklower		tp_detach($P);
37836396Ssklower	}
37936396Ssklower;
38036396Ssklower
38136396Ssklower/* applicable in TP4, TP0 */
38236396SsklowerTP_CRSENT		<==		TP_CLOSED								T_CONN_req
38336396Ssklower	DEFAULT
38436396Ssklower	{
38536396Ssklower		int error;
38636396Ssklower		struct mbuf *data = MNULL;
38736396Ssklower
38836396Ssklower		IFTRACE(D_CONN)
38937469Ssklower			tptrace(TPPTmisc, "T_CONN_req flags ucddata", (int)$P.tp_flags,
39037469Ssklower			$P.tp_ucddata, 0, 0);
39136396Ssklower		ENDTRACE
39237469Ssklower		data =  MCPY($P.tp_ucddata, M_WAIT);
39337469Ssklower		if (data) {
39436396Ssklower			IFDEBUG(D_CONN)
39536396Ssklower				printf("T_CONN_req.trans m_copy cc 0x%x\n",
39637469Ssklower					$P.tp_ucddata);
39737469Ssklower				dump_mbuf(data, "sosnd @ T_CONN_req");
39836396Ssklower			ENDDEBUG
39936396Ssklower		}
40036396Ssklower
40136396Ssklower		if (error = tp_emit(CR_TPDU_type, $P, 0, 0, data) )
40236396Ssklower			return error; /* driver WON'T change state; will return error */
40336396Ssklower
40451204Ssklower		$P.tp_refstate = REF_OPEN; /* has timers */
40536396Ssklower		if($P.tp_class != TP_CLASS_0) {
40636396Ssklower			$P.tp_retrans = $P.tp_Nretrans;
40751204Ssklower			tp_ctimeout($P, TM_retrans, (int)$P.tp_cr_ticks);
40836396Ssklower		}
40936396Ssklower	}
41036396Ssklower;
41136396Ssklower
41236396Ssklower/* applicable in TP4, TP0, but state TP_AKWAIT doesn't apply to TP0 */
41336396SsklowerTP_REFWAIT 		<==		[ TP_CRSENT, TP_AKWAIT, TP_OPEN ] 			DR_TPDU
41436396Ssklower	DEFAULT
41536396Ssklower	{
41648739Ssklower		sbflush(&$P.tp_Xrcv); /* purge non-delivered data data */
41748739Ssklower		if ($$.e_datalen > 0) {
41836396Ssklower			sbappendrecord(&$P.tp_Xrcv, $$.e_data);
41936396Ssklower			$$.e_data = MNULL;
42036396Ssklower		}
421*51208Ssklower		if ($P.tp_state == TP_OPEN)
422*51208Ssklower			tp_indicate(T_DISCONNECT, $P, 0);
423*51208Ssklower		else {
424*51208Ssklower			int so_error = ECONNREFUSED;
425*51208Ssklower			if ($$.e_reason != (E_TP_NO_SESSION ^ TP_ERROR_MASK) &&
426*51208Ssklower			    $$.e_reason != (E_TP_NO_CR_ON_NC ^ TP_ERROR_MASK) &&
427*51208Ssklower			    $$.e_reason != (E_TP_REF_OVERFLOW ^ TP_ERROR_MASK))
428*51208Ssklower				so_error = ECONNABORTED;
429*51208Ssklower			tp_indicate(T_DISCONNECT, $P, so_error);
430*51208Ssklower		}
43136396Ssklower		tp_soisdisconnected($P);
43236396Ssklower		if ($P.tp_class != TP_CLASS_0) {
43336396Ssklower			if ($P.tp_state == TP_OPEN ) {
43451204Ssklower				tp_euntimeout($P, TM_data_retrans); /* all */
43551204Ssklower				tp_cuntimeout($P, TM_retrans);
43651204Ssklower				tp_cuntimeout($P, TM_inact);
43751204Ssklower				tp_cuntimeout($P, TM_sendack);
43836396Ssklower			}
43951204Ssklower			tp_cuntimeout($P, TM_retrans);
44036396Ssklower			if( $$.e_sref !=  0 )
44136396Ssklower				(void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL);
44236396Ssklower		}
44336396Ssklower	}
44436396Ssklower;
44536396Ssklower
44636396SsklowerSAME 			<==		TP_CLOSED 									DR_TPDU
44736396Ssklower	DEFAULT
44836396Ssklower	{
44936396Ssklower		if( $$.e_sref != 0 )
45036396Ssklower			(void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL);
45136396Ssklower		/* reference timer already set - reset it to be safe (???) */
45251204Ssklower		tp_euntimeout($P, TM_reference); /* all */
45351204Ssklower		tp_etimeout($P, TM_reference, (int)$P.tp_refer_ticks);
45436396Ssklower	}
45536396Ssklower;
45636396Ssklower
45736396Ssklower/* NBS(34) */
45836396SsklowerTP_REFWAIT 		<==  	TP_CRSENT  									ER_TPDU
45936396Ssklower	DEFAULT
46036396Ssklower	{
46151204Ssklower		tp_cuntimeout($P, TM_retrans);
46248739Ssklower		tp_indicate(ER_TPDU, $P, $$.e_reason);
46336396Ssklower		tp_soisdisconnected($P);
46436396Ssklower	}
46536396Ssklower;
46636396Ssklower
46736396Ssklower/* NBS(27) */
46836396SsklowerTP_REFWAIT		<==		TP_CLOSING									DR_TPDU
46936396Ssklower	DEFAULT
47036396Ssklower	{
47151204Ssklower		tp_cuntimeout($P, TM_retrans);
47236396Ssklower		tp_soisdisconnected($P);
47336396Ssklower	}
47436396Ssklower;
47536396Ssklower/* these two transitions are the same but can't be combined because xebec
47636396Ssklower * can't handle the use of $$.e_reason if they're combined
47736396Ssklower */
47836396Ssklower/* NBS(27) */
47936396SsklowerTP_REFWAIT		<==		TP_CLOSING									ER_TPDU
48036396Ssklower	DEFAULT
48136396Ssklower	{
48248739Ssklower		tp_indicate(ER_TPDU, $P, $$.e_reason);
48351204Ssklower		tp_cuntimeout($P, TM_retrans);
48436396Ssklower		tp_soisdisconnected($P);
48536396Ssklower	}
48636396Ssklower;
48736396Ssklower/* NBS(27) */
48836396SsklowerTP_REFWAIT		<==		TP_CLOSING									DC_TPDU
48936396Ssklower	DEFAULT
49036396Ssklower	{
49151204Ssklower		tp_cuntimeout($P, TM_retrans);
49236396Ssklower		tp_soisdisconnected($P);
49336396Ssklower	}
49436396Ssklower;
49536396Ssklower
49636396Ssklower/* NBS(21) */
49736396SsklowerSAME 			<== 	TP_CLOSED 						[ CC_TPDU, CR_TPDU ]
49836396Ssklower	DEFAULT
49936396Ssklower	{	/* don't ask me why we have to do this - spec says so */
50036396Ssklower		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_NO_SESSION, MNULL);
50136396Ssklower		/* don't bother with retransmissions of the DR */
50236396Ssklower	}
50336396Ssklower;
50436396Ssklower
50536396Ssklower/* NBS(34) */
50636396SsklowerTP_REFWAIT 		<== 	TP_OPEN  				 					ER_TPDU
50736396Ssklower	($P.tp_class == TP_CLASS_0)
50836396Ssklower	{
50936396Ssklower		tp_soisdisconnecting($P.tp_sock);
51048739Ssklower		tp_indicate(ER_TPDU, $P, $$.e_reason);
51136396Ssklower		tp_soisdisconnected($P);
51236396Ssklower		tp_netcmd( $P, CONN_CLOSE );
51336396Ssklower	}
51436396Ssklower;
51536396Ssklower
51636396SsklowerTP_CLOSING 		<== 	[ TP_AKWAIT, TP_OPEN ]  					ER_TPDU
51736396Ssklower	DEFAULT
51836396Ssklower	{
51936396Ssklower		if ($P.tp_state == TP_OPEN) {
52051204Ssklower			tp_euntimeout($P, TM_data_retrans); /* all */
52151204Ssklower			tp_cuntimeout($P, TM_inact);
52251204Ssklower			tp_cuntimeout($P, TM_sendack);
52336396Ssklower		}
52436396Ssklower		tp_soisdisconnecting($P.tp_sock);
52548739Ssklower		tp_indicate(ER_TPDU, $P, $$.e_reason);
52636396Ssklower		$P.tp_retrans = $P.tp_Nretrans;
52751204Ssklower		tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
52836396Ssklower		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_PROTO_ERR, MNULL);
52936396Ssklower	}
53036396Ssklower;
53136396Ssklower/* NBS(6) */
53236396SsklowerTP_OPEN			<==		TP_CRSENT									CC_TPDU
53336396Ssklower	($P.tp_class == TP_CLASS_0)
53436396Ssklower	{
53551204Ssklower		tp_cuntimeout($P, TM_retrans);
53636396Ssklower		IncStat(ts_tp0_conn);
53736396Ssklower		$P.tp_fcredit = 1;
53836396Ssklower		soisconnected($P.tp_sock);
53936396Ssklower	}
54036396Ssklower;
54136396Ssklower
54236396SsklowerTP_OPEN			<==		TP_CRSENT									CC_TPDU
54336396Ssklower	DEFAULT
54436396Ssklower	{
54536396Ssklower		IFDEBUG(D_CONN)
54636396Ssklower			printf("trans: CC_TPDU in CRSENT state flags 0x%x\n",
54736396Ssklower				(int)$P.tp_flags);
54836396Ssklower		ENDDEBUG
54936396Ssklower		IncStat(ts_tp4_conn);
55036396Ssklower		$P.tp_fref = $$.e_sref;
55136396Ssklower		$P.tp_fcredit = $$.e_cdt;
55247281Ssklower		if (($P.tp_rx_strat & TPRX_FASTSTART) && ($$.e_cdt > 0))
55351204Ssklower			$P.tp_cong_win = $$.e_cdt * $P.tp_l_tpdusize;
55436396Ssklower		tp_getoptions($P);
55551204Ssklower		tp_cuntimeout($P, TM_retrans);
55637469Ssklower		if ($P.tp_ucddata) {
55736396Ssklower			IFDEBUG(D_CONN)
55837469Ssklower				printf("dropping user connect data cc 0x%x\n",
55937469Ssklower					$P.tp_ucddata->m_len);
56036396Ssklower			ENDDEBUG
56137469Ssklower			m_freem($P.tp_ucddata);
56237469Ssklower			$P.tp_ucddata = 0;
56336396Ssklower		}
56436396Ssklower		soisconnected($P.tp_sock);
56536396Ssklower		if ($$.e_datalen > 0) {
56636396Ssklower			ASSERT($P.tp_Xrcv.sb_cc == 0); /* should be empty */
56736396Ssklower			sbappendrecord(&$P.tp_Xrcv, $$.e_data);
56836396Ssklower			$$.e_data = MNULL;
56936396Ssklower		}
57036396Ssklower
57136396Ssklower		(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
57251204Ssklower		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
57336396Ssklower	}
57436396Ssklower;
57536396Ssklower
57636396Ssklower/* TP4 only */
57736396SsklowerSAME			<==		TP_CRSENT									TM_retrans
57836396Ssklower	(	$P.tp_retrans > 0 )
57936396Ssklower	{
58036396Ssklower		struct mbuf *data = MNULL;
58136396Ssklower		int error;
58236396Ssklower
58336396Ssklower		IncStat(ts_retrans_cr);
58451204Ssklower		$P.tp_cong_win = 1 * $P.tp_l_tpdusize;
58537469Ssklower		data = MCPY($P.tp_ucddata, M_NOWAIT);
58637469Ssklower		if($P.tp_ucddata) {
58736396Ssklower			IFDEBUG(D_CONN)
58837469Ssklower				printf("TM_retrans.trans m_copy cc 0x%x\n", data);
58937469Ssklower				dump_mbuf($P.tp_ucddata, "sosnd @ TM_retrans");
59036396Ssklower			ENDDEBUG
59136396Ssklower			if( data == MNULL )
59236396Ssklower				return ENOBUFS;
59336396Ssklower		}
59436396Ssklower
59536396Ssklower		$P.tp_retrans --;
59636396Ssklower		if( error = tp_emit(CR_TPDU_type, $P, 0, 0, data) ) {
59736396Ssklower			$P.tp_sock->so_error = error;
59836396Ssklower		}
59951204Ssklower		tp_ctimeout($P, TM_retrans, (int)$P.tp_cr_ticks);
60036396Ssklower	}
60136396Ssklower;
60236396Ssklower
60336396Ssklower/* TP4 only  */
60436396SsklowerTP_REFWAIT		<==		TP_CRSENT									TM_retrans
60536396Ssklower	DEFAULT /* no more CR retransmissions */
60636396Ssklower	{
60736396Ssklower		IncStat(ts_conn_gaveup);
60836396Ssklower		$P.tp_sock->so_error = ETIMEDOUT;
60936396Ssklower		tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
61036396Ssklower		tp_soisdisconnected($P);
61136396Ssklower	}
61236396Ssklower;
61336396Ssklower
61436396Ssklower/* TP4 only */
61536396SsklowerSAME 			<==	 TP_AKWAIT											CR_TPDU
61636396Ssklower	DEFAULT
61736396Ssklower	/* duplicate CR (which doesn't really exist in the context of
61836396Ssklower	 * a connectionless network layer)
61936396Ssklower	 * Doesn't occur in class 0.
62036396Ssklower	 */
62136396Ssklower	{
62236396Ssklower		int error;
62337469Ssklower		struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
62436396Ssklower
62537469Ssklower		if( error = tp_emit(CC_TPDU_type, $P, 0, 0, data) ) {
62636396Ssklower			$P.tp_sock->so_error = error;
62736396Ssklower		}
62836396Ssklower		$P.tp_retrans = $P.tp_Nretrans;
62951204Ssklower		tp_ctimeout($P, TM_retrans, (int)$P.tp_cc_ticks);
63036396Ssklower	}
63136396Ssklower;
63236396Ssklower
63336396Ssklower/* TP4 only */
63436396SsklowerTP_OPEN			<==		TP_AKWAIT 										DT_TPDU
63536396Ssklower	( IN_RWINDOW( $P, $$.e_seq,
63636396Ssklower					$P.tp_rcvnxt, SEQ($P, $P.tp_rcvnxt + $P.tp_lcredit)) )
63736396Ssklower	{
63836396Ssklower		int doack;
63936396Ssklower
64037469Ssklower		/*
64137469Ssklower		 * Get rid of any confirm or connect data, so that if we
64237469Ssklower		 * crash or close, it isn't thought of as disconnect data.
64337469Ssklower		 */
64437469Ssklower		if ($P.tp_ucddata) {
64537469Ssklower			m_freem($P.tp_ucddata);
64637469Ssklower			$P.tp_ucddata = 0;
64736396Ssklower		}
64851204Ssklower		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
64951204Ssklower		tp_cuntimeout($P, TM_retrans);
65036396Ssklower		soisconnected($P.tp_sock);
65151204Ssklower		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
65236396Ssklower
65336396Ssklower		/* see also next 2 transitions, if you make any changes */
65436396Ssklower
65536396Ssklower		doack = tp_stash($P, $E);
65636396Ssklower		IFDEBUG(D_DATA)
65736396Ssklower			printf("tp_stash returns %d\n",doack);
65836396Ssklower		ENDDEBUG
65936396Ssklower
66051204Ssklower		if (doack) {
66136396Ssklower			(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL );
66251204Ssklower			tp_ctimeout($P, TM_sendack, (int)$P.tp_keepalive_ticks);
66336396Ssklower		} else
66451204Ssklower			tp_ctimeout( $P, TM_sendack, (int)$P.tp_sendack_ticks);
66536396Ssklower
66636396Ssklower		IFDEBUG(D_DATA)
66736396Ssklower			printf("after stash calling sbwakeup\n");
66836396Ssklower		ENDDEBUG
66936396Ssklower	}
67036396Ssklower;
67136396Ssklower
67236396SsklowerSAME			<==		TP_OPEN 									DT_TPDU
67336396Ssklower	( $P.tp_class == TP_CLASS_0 )
67436396Ssklower	{
67536396Ssklower		tp0_stash($P, $E);
67636396Ssklower		sbwakeup( &$P.tp_sock->so_rcv );
67736396Ssklower
67836396Ssklower		IFDEBUG(D_DATA)
67936396Ssklower			printf("after stash calling sbwakeup\n");
68036396Ssklower		ENDDEBUG
68136396Ssklower	}
68236396Ssklower;
68336396Ssklower
68436396Ssklower/* TP4 only */
68536396SsklowerSAME			<==		TP_OPEN 									DT_TPDU
68636396Ssklower	( IN_RWINDOW( $P, $$.e_seq,
68736396Ssklower					$P.tp_rcvnxt, SEQ($P, $P.tp_rcvnxt + $P.tp_lcredit)) )
68836396Ssklower	{
68936396Ssklower		int doack; /* tells if we must ack immediately */
69036396Ssklower
69151204Ssklower		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
69236396Ssklower		sbwakeup( &$P.tp_sock->so_rcv );
69336396Ssklower
69436396Ssklower		doack = tp_stash($P, $E);
69536396Ssklower		IFDEBUG(D_DATA)
69636396Ssklower			printf("tp_stash returns %d\n",doack);
69736396Ssklower		ENDDEBUG
69836396Ssklower
69936396Ssklower		if(doack)
70036396Ssklower			(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL );
70136396Ssklower		else
70251204Ssklower			tp_ctimeout_MIN( $P, TM_sendack, (int)$P.tp_sendack_ticks);
70336396Ssklower
70436396Ssklower		IFDEBUG(D_DATA)
70536396Ssklower			printf("after stash calling sbwakeup\n");
70636396Ssklower		ENDDEBUG
70736396Ssklower	}
70836396Ssklower;
70936396Ssklower
71036396Ssklower/* Not in window  - we must ack under certain circumstances, namely
71136396Ssklower * a) if the seq number is below lwe but > lwe - (max credit ever given)
71236396Ssklower * (to handle lost acks) Can use max-possible-credit for this ^^^.
71336396Ssklower * and
71436396Ssklower * b) seq number is > uwe but < uwe + previously sent & withdrawn credit
71536396Ssklower *
71636396Ssklower * (see 12.2.3.8.1 of ISO spec, p. 73)
71736396Ssklower * We just always ack.
71836396Ssklower */
71936396Ssklower/* TP4 only */
72036396SsklowerSAME 			<== 	[ TP_OPEN, TP_AKWAIT ]							DT_TPDU
72136396Ssklower	DEFAULT /* Not in window */
72236396Ssklower	{
72336396Ssklower		IFTRACE(D_DATA)
72436396Ssklower			tptrace(TPPTmisc, "NIW seq rcvnxt lcredit ",
72536396Ssklower				$$.e_seq, $P.tp_rcvnxt, $P.tp_lcredit, 0);
72636396Ssklower		ENDTRACE
72736396Ssklower		IncStat(ts_dt_niw);
72836396Ssklower		m_freem($$.e_data);
72951204Ssklower		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
73036396Ssklower		(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL );
73136396Ssklower	}
73236396Ssklower;
73336396Ssklower
73436396Ssklower/* TP4 only */
73536396SsklowerTP_OPEN			<==		TP_AKWAIT										AK_TPDU
73636396Ssklower	DEFAULT
73736396Ssklower	{
73837469Ssklower		if ($P.tp_ucddata) {
73937469Ssklower			m_freem($P.tp_ucddata);
74037469Ssklower			$P.tp_ucddata = 0;
74136396Ssklower		}
74236396Ssklower		(void) tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq);
74351204Ssklower		tp_cuntimeout($P, TM_retrans);
74436396Ssklower
74536396Ssklower		soisconnected($P.tp_sock);
74636396Ssklower		IFTRACE(D_CONN)
74736396Ssklower			struct socket *so = $P.tp_sock;
74836396Ssklower			tptrace(TPPTmisc,
74936396Ssklower			"called sosiconn: so so_state rcv.sb_sel rcv.sb_flags",
75036396Ssklower				so, so->so_state, so->so_rcv.sb_sel, so->so_rcv.sb_flags);
75136396Ssklower			tptrace(TPPTmisc,
75236396Ssklower			"called sosiconn 2: so_qlen so_error so_rcv.sb_cc so_head",
75336396Ssklower				so->so_qlen, so->so_error, so->so_rcv.sb_cc, so->so_head);
75436396Ssklower		ENDTRACE
75536396Ssklower
75651204Ssklower		tp_ctimeout($P, TM_sendack, (int)$P.tp_keepalive_ticks);
75751204Ssklower		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
75836396Ssklower	}
75936396Ssklower;
76036396Ssklower
76136396Ssklower/* TP4 only */
76236396SsklowerTP_OPEN 		<== 	[ TP_OPEN, TP_AKWAIT ]						XPD_TPDU
76347281Ssklower	($P.tp_Xrcvnxt == $$.e_seq)
76436396Ssklower	{
76536396Ssklower		if( $P.tp_state == TP_AKWAIT ) {
76637469Ssklower			if ($P.tp_ucddata) {
76737469Ssklower				m_freem($P.tp_ucddata);
76837469Ssklower				$P.tp_ucddata = 0;
76936396Ssklower			}
77051204Ssklower			tp_cuntimeout($P, TM_retrans);
77136396Ssklower			soisconnected($P.tp_sock);
77251204Ssklower			tp_ctimeout($P, TM_sendack, (int)$P.tp_keepalive_ticks);
77351204Ssklower			tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
77436396Ssklower		}
77536396Ssklower		IFTRACE(D_XPD)
77636396Ssklower		tptrace(TPPTmisc, "XPD tpdu accepted Xrcvnxt, e_seq datalen m_len\n",
77736396Ssklower				$P.tp_Xrcvnxt,$$.e_seq,  $$.e_datalen, $$.e_data->m_len);
77836396Ssklower		ENDTRACE
77936396Ssklower
78037469Ssklower		$P.tp_sock->so_state |= SS_RCVATMARK;
78147281Ssklower		$$.e_data->m_flags |= M_EOR;
78237469Ssklower		sbinsertoob(&$P.tp_Xrcv, $$.e_data);
78336396Ssklower		IFDEBUG(D_XPD)
78436396Ssklower			dump_mbuf($$.e_data, "XPD TPDU: tp_Xrcv");
78536396Ssklower		ENDDEBUG
78636396Ssklower		tp_indicate(T_XDATA, $P, 0);
78736396Ssklower		sbwakeup( &$P.tp_Xrcv );
78836396Ssklower
78936396Ssklower		(void) tp_emit(XAK_TPDU_type, $P, $P.tp_Xrcvnxt, 0, MNULL);
79036396Ssklower		SEQ_INC($P, $P.tp_Xrcvnxt);
79136396Ssklower	}
79236396Ssklower;
79336396Ssklower
79436396Ssklower/* TP4 only */
79536396SsklowerSAME			<==		TP_OPEN 									T_USR_Xrcvd
79636396Ssklower	DEFAULT
79736396Ssklower	{
79836396Ssklower		if( $P.tp_Xrcv.sb_cc == 0 ) {
79936396Ssklower			/* kludge for select(): */
80037469Ssklower			/* $P.tp_sock->so_state &= ~SS_OOBAVAIL; */
80136396Ssklower		}
80236396Ssklower	}
80336396Ssklower	/* OLD WAY:
80436396Ssklower	 * Ack only after the user receives the XPD.  This is better for
80536396Ssklower	 * users that use one XPD right after another.
80636396Ssklower	 * Acking right away (the NEW WAY, see the prev. transition) is
80736396Ssklower	 * better for occasional * XPD, when the receiving user doesn't
80836396Ssklower	 * want to read the XPD immediately (which is session's behavior).
80936396Ssklower	 *
81036396Ssklower		int error = tp_emit(XAK_TPDU_type, $P, $P.tp_Xrcvnxt, 0, MNULL);
81136396Ssklower		SEQ_INC($P, $P.tp_Xrcvnxt);
81236396Ssklower		return error;
81336396Ssklower	*/
81436396Ssklower;
81536396Ssklower
81636396Ssklower/* NOTE: presently if the user doesn't read the connection data
81736396Ssklower * before and expedited data PDU comes in, the connection data will
81836396Ssklower * be dropped. This is a bug.  To avoid it, we need somewhere else
81936396Ssklower * to put the connection data.
82036396Ssklower * On the other hand, we need not to have it sitting around forever.
82136396Ssklower * This is a problem with the idea of trying to accommodate
82236396Ssklower * data on connect w/ a passive-open user interface.
82336396Ssklower */
82436396Ssklower/* TP4 only */
82536396Ssklower
82636396SsklowerSAME	 		<== 	[ TP_AKWAIT, TP_OPEN ] 							XPD_TPDU
82736396Ssklower	DEFAULT /* not in window or cdt==0 */
82836396Ssklower	{
82936396Ssklower		IFTRACE(D_XPD)
83036396Ssklower			tptrace(TPPTmisc, "XPD tpdu niw (Xrcvnxt, e_seq) or not cdt (cc)\n",
83136396Ssklower				$P.tp_Xrcvnxt, $$.e_seq,  $P.tp_Xrcv.sb_cc , 0);
83236396Ssklower		ENDTRACE
83336396Ssklower		if( $P.tp_Xrcvnxt != $$.e_seq )
83436396Ssklower			IncStat(ts_xpd_niw);
83536396Ssklower		if( $P.tp_Xrcv.sb_cc ) {
83636396Ssklower			/* might as well kick 'em again */
83736396Ssklower			tp_indicate(T_XDATA, $P, 0);
83836396Ssklower			IncStat(ts_xpd_dup);
83936396Ssklower		}
84036396Ssklower		m_freem($$.e_data);
84151204Ssklower		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
84236396Ssklower		/* don't send an xack because the xak gives "last one received", not
84336396Ssklower		 * "next one i expect" (dumb)
84436396Ssklower		 */
84536396Ssklower	}
84636396Ssklower;
84736396Ssklower
84836396Ssklower/* Occurs (AKWAIT, OPEN) when parent (listening) socket gets aborted, and tries
84936396Ssklower * to detach all its "children"
85036396Ssklower * Also (CRSENT) when user kills a job that's doing a connect()
85136396Ssklower */
85236396SsklowerTP_REFWAIT		<== 	TP_CRSENT 										T_DETACH
85336396Ssklower	($P.tp_class == TP_CLASS_0)
85436396Ssklower	{
85536396Ssklower		struct socket *so = $P.tp_sock;
85636396Ssklower
85736396Ssklower		/* detach from parent socket so it can finish closing */
85836396Ssklower		if (so->so_head) {
85936396Ssklower			if (!soqremque(so, 0) && !soqremque(so, 1))
86036396Ssklower				panic("tp: T_DETACH");
86136396Ssklower			so->so_head = 0;
86236396Ssklower		}
86336396Ssklower		tp_soisdisconnecting($P.tp_sock);
86436396Ssklower		tp_netcmd( $P, CONN_CLOSE);
86536396Ssklower		tp_soisdisconnected($P);
86636396Ssklower	}
86736396Ssklower;
86836396Ssklower
86936396Ssklower/* TP4 only */
87038841SsklowerTP_CLOSING		<== [ TP_CLOSING, TP_AKWAIT, TP_CRSENT, TP_CONFIRMING ]	T_DETACH
87136396Ssklower	DEFAULT
87236396Ssklower	{
87336396Ssklower		struct socket *so = $P.tp_sock;
87437469Ssklower		struct mbuf *data = MNULL;
87536396Ssklower
87636396Ssklower		/* detach from parent socket so it can finish closing */
87736396Ssklower		if (so->so_head) {
87836396Ssklower			if (!soqremque(so, 0) && !soqremque(so, 1))
87936396Ssklower				panic("tp: T_DETACH");
88036396Ssklower			so->so_head = 0;
88136396Ssklower		}
88236396Ssklower		if ($P.tp_state != TP_CLOSING) {
88336396Ssklower			tp_soisdisconnecting($P.tp_sock);
88437469Ssklower			data = MCPY($P.tp_ucddata, M_NOWAIT);
88537469Ssklower			(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_NORMAL_DISC, data);
88636396Ssklower			$P.tp_retrans = $P.tp_Nretrans;
88751204Ssklower			tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
88836396Ssklower		}
88936396Ssklower	}
89036396Ssklower;
89136396Ssklower
89236396SsklowerTP_REFWAIT		<==		[ TP_OPEN, TP_CRSENT ]		 	  			T_DISC_req
89336396Ssklower	( $P.tp_class == TP_CLASS_0 )
89436396Ssklower	{
89536396Ssklower		tp_soisdisconnecting($P.tp_sock);
89636396Ssklower		tp_netcmd( $P, CONN_CLOSE);
89736396Ssklower		tp_soisdisconnected($P);
89836396Ssklower	}
89936396Ssklower;
90036396Ssklower
90136396Ssklower/* TP4 only */
90238841SsklowerTP_CLOSING		<==	[ TP_AKWAIT, TP_OPEN, TP_CRSENT, TP_CONFIRMING ]  T_DISC_req
90336396Ssklower	DEFAULT
90436396Ssklower	{
90537469Ssklower		struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
90636396Ssklower
90736396Ssklower		if($P.tp_state == TP_OPEN) {
90851204Ssklower			tp_euntimeout($P, TM_data_retrans); /* all */
90951204Ssklower			tp_cuntimeout($P, TM_inact);
91051204Ssklower			tp_cuntimeout($P, TM_sendack);
91136396Ssklower		}
91237469Ssklower		if (data) {
91336396Ssklower			IFDEBUG(D_CONN)
91437469Ssklower				printf("T_DISC_req.trans tp_ucddata 0x%x\n",
91537469Ssklower					$P.tp_ucddata);
91637469Ssklower				dump_mbuf(data, "ucddata @ T_DISC_req");
91736396Ssklower			ENDDEBUG
91836396Ssklower		}
91936396Ssklower		tp_soisdisconnecting($P.tp_sock);
92036396Ssklower		$P.tp_retrans = $P.tp_Nretrans;
92151204Ssklower		tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
92236396Ssklower
92336396Ssklower		if( trick_hc )
92436396Ssklower			return tp_emit(DR_TPDU_type, $P, 0, $$.e_reason, data);
92536396Ssklower	}
92636396Ssklower;
92736396Ssklower
92836396Ssklower/* TP4 only */
92936396SsklowerSAME			<==		TP_AKWAIT									TM_retrans
93036396Ssklower	( $P.tp_retrans > 0 )
93136396Ssklower	{
93236396Ssklower		int error;
93337469Ssklower		struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
93436396Ssklower
93536396Ssklower		IncStat(ts_retrans_cc);
93636396Ssklower		$P.tp_retrans --;
93751204Ssklower		$P.tp_cong_win = 1 * $P.tp_l_tpdusize;
93839921Ssklower
93937469Ssklower		if( error = tp_emit(CC_TPDU_type, $P, 0, 0, data) )
94036396Ssklower			$P.tp_sock->so_error = error;
94151204Ssklower		tp_ctimeout($P, TM_retrans, (int)$P.tp_cc_ticks);
94236396Ssklower	}
94336396Ssklower;
94436396Ssklower
94536396Ssklower/* TP4 only */
94636396SsklowerTP_CLOSING		<==		TP_AKWAIT									TM_retrans
94736396Ssklower	DEFAULT  /* out of time */
94836396Ssklower	{
94936396Ssklower		IncStat(ts_conn_gaveup);
95036396Ssklower		tp_soisdisconnecting($P.tp_sock);
95136396Ssklower		$P.tp_sock->so_error = ETIMEDOUT;
95236396Ssklower		tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
95336396Ssklower		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_CONGEST, MNULL);
95436396Ssklower		$P.tp_retrans = $P.tp_Nretrans;
95551204Ssklower		tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
95636396Ssklower	}
95736396Ssklower;
95836396Ssklower
95936396Ssklower/* the retrans timers had better go off BEFORE the inactivity timer does,
96036396Ssklower * if transmissions are going on.
96136396Ssklower * (i.e., TM_inact should be greater than timer for all retrans plus ack
96236396Ssklower * turnaround)
96336396Ssklower */
96436396Ssklower/* TP4 only */
96536396SsklowerTP_CLOSING 		<==		TP_OPEN		   [ TM_inact, TM_retrans, TM_data_retrans ]
96636396Ssklower	DEFAULT
96736396Ssklower	{
96851204Ssklower		tp_euntimeout($P, TM_data_retrans); /* all */
96951204Ssklower		tp_cuntimeout($P, TM_inact);
97051204Ssklower		tp_cuntimeout($P, TM_sendack);
97136396Ssklower
97236396Ssklower		IncStat(ts_conn_gaveup);
97336396Ssklower		tp_soisdisconnecting($P.tp_sock);
97436396Ssklower		$P.tp_sock->so_error = ETIMEDOUT;
97536396Ssklower		tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
97636396Ssklower		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_CONGEST_2, MNULL);
97736396Ssklower		$P.tp_retrans = $P.tp_Nretrans;
97851204Ssklower		tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
97936396Ssklower	}
98036396Ssklower;
98136396Ssklower
98236396Ssklower/* TP4 only */
98336396SsklowerSAME			<==		TP_OPEN										TM_retrans
98436396Ssklower	( $P.tp_retrans > 0 )
98536396Ssklower	{
98651204Ssklower		$P.tp_cong_win = 1 * $P.tp_l_tpdusize;
98736396Ssklower		/* resume XPD */
98836396Ssklower		if	( $P.tp_Xsnd.sb_mb )  {
98937469Ssklower			struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc);
99051204Ssklower			int shift;
99136396Ssklower
99236396Ssklower			IFTRACE(D_XPD)
99351204Ssklower				tptrace(TPPTmisc, "XPD retrans: Xuna Xsndnxt sndnxt snduna",
99451204Ssklower					$P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndnxt,
99536396Ssklower					$P.tp_snduna);
99636396Ssklower			ENDTRACE
99736396Ssklower			IFDEBUG(D_XPD)
99836396Ssklower				dump_mbuf(m, "XPD retrans emitting M");
99936396Ssklower			ENDDEBUG
100036396Ssklower			IncStat(ts_retrans_xpd);
100136396Ssklower			$P.tp_retrans --;
100251204Ssklower			shift = max($P.tp_Nretrans - $P.tp_retrans, 6);
100336396Ssklower			(void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
100451204Ssklower			tp_ctimeout($P, TM_retrans, ((int)$P.tp_dt_ticks) << shift);
100536396Ssklower		}
100636396Ssklower	}
100736396Ssklower;
100836396Ssklower
100936396Ssklower/* TP4 only */
101036396SsklowerSAME 			<==		TP_OPEN									TM_data_retrans
101151204Ssklower	($P.tp_rxtshift < TP_NRETRANS)
101236396Ssklower	{
101351204Ssklower		$P.tp_rxtshift++;
101451204Ssklower		(void) tp_data_retrans($P);
101536396Ssklower	}
101636396Ssklower;
101736396Ssklower
101836396Ssklower/* TP4 only */
101936396SsklowerSAME	 		<==		TP_CLOSING									TM_retrans
102036396Ssklower	(	$P.tp_retrans > 0 )
102136396Ssklower	{
102236396Ssklower		$P.tp_retrans --;
102336396Ssklower		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_DR_NO_REAS, MNULL);
102436396Ssklower		IncStat(ts_retrans_dr);
102551204Ssklower		tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
102636396Ssklower	}
102736396Ssklower;
102836396Ssklower
102936396Ssklower/* TP4 only */
103036396SsklowerTP_REFWAIT 		<==		TP_CLOSING									TM_retrans
103136396Ssklower	DEFAULT	/* no more retrans - gave up */
103236396Ssklower	{
103336396Ssklower		$P.tp_sock->so_error = ETIMEDOUT;
103451204Ssklower		$P.tp_refstate = REF_FROZEN;
103536396Ssklower		tp_recycle_tsuffix( $P );
103651204Ssklower		tp_etimeout($P, TM_reference, (int)$P.tp_refer_ticks);
103736396Ssklower	}
103836396Ssklower;
103936396Ssklower
104036396Ssklower/*
104136396Ssklower * The resources are kept around until the ref timer goes off.
104236396Ssklower * The suffices are wiped out sooner so they can be reused right away.
104336396Ssklower */
104436396Ssklower/* applicable in TP4, TP0 */
104536396SsklowerTP_CLOSED 		<==		TP_REFWAIT 									TM_reference
104636396Ssklower	DEFAULT
104736396Ssklower	{
104836396Ssklower		tp_freeref($P.tp_refp);
104936396Ssklower		tp_detach($P);
105036396Ssklower	}
105136396Ssklower;
105236396Ssklower
105336396Ssklower/* applicable in TP4, TP0 */
105436396Ssklower/* A duplicate CR from connectionless network layer can't happen */
105536396SsklowerSAME 			<== 	TP_OPEN 							[ CR_TPDU, CC_TPDU ]
105636396Ssklower	DEFAULT
105736396Ssklower	{
105836396Ssklower		if( $P.tp_class != TP_CLASS_0) {
105951204Ssklower			tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
106036396Ssklower			if ( $E.ev_number == CC_TPDU )
106136396Ssklower				(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
106236396Ssklower		}
106336396Ssklower		/* ignore it if class 0 - state tables are blank for this */
106436396Ssklower	}
106536396Ssklower;
106636396Ssklower
106736396Ssklower/* applicable in TP4, TP0 */
106836396SsklowerSAME			<== 	TP_OPEN									T_DATA_req
106936396Ssklower	DEFAULT
107036396Ssklower	{
107136396Ssklower		IFTRACE(D_DATA)
107251204Ssklower			tptrace(TPPTmisc, "T_DATA_req sndnxt snduna fcredit, tpcb",
107351204Ssklower				$P.tp_sndnxt, $P.tp_snduna, $P.tp_fcredit, $P);
107436396Ssklower		ENDTRACE
107536396Ssklower
107636396Ssklower		tp_send($P);
107736396Ssklower	}
107836396Ssklower;
107936396Ssklower
108036396Ssklower/* TP4 only */
108136396SsklowerSAME			<==		TP_OPEN										T_XPD_req
108236396Ssklower	DEFAULT
108336396Ssklower		/* T_XPD_req was issued by sosend iff xpd socket buf was empty
108436396Ssklower		 * at time of sosend(),
108536396Ssklower		 * AND (which means) there were no unacknowledged XPD tpdus outstanding!
108636396Ssklower		 */
108736396Ssklower	{
108836396Ssklower		int error = 0;
108936396Ssklower
109036396Ssklower		/* resume XPD */
109136396Ssklower		if	( $P.tp_Xsnd.sb_mb )  {
109237469Ssklower			struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc);
109336396Ssklower			/* m_copy doesn't preserve the m_xlink field, but at this pt.
109436396Ssklower			 * that doesn't matter
109536396Ssklower			 */
109636396Ssklower
109736396Ssklower			IFTRACE(D_XPD)
109851204Ssklower				tptrace(TPPTmisc, "XPD req: Xuna Xsndnxt sndnxt snduna",
109951204Ssklower					$P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndnxt,
110036396Ssklower					$P.tp_snduna);
110136396Ssklower			ENDTRACE
110236396Ssklower			IFDEBUG(D_XPD)
110336396Ssklower				printf("T_XPD_req: sb_cc 0x%x\n", $P.tp_Xsnd.sb_cc);
110436396Ssklower				dump_mbuf(m, "XPD req emitting M");
110536396Ssklower			ENDDEBUG
110636396Ssklower			error =
110736396Ssklower				tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
110836396Ssklower			$P.tp_retrans = $P.tp_Nretrans;
110951204Ssklower
111051204Ssklower			tp_ctimeout($P, TM_retrans, (int)$P.tp_rxtcur);
111136396Ssklower			SEQ_INC($P, $P.tp_Xsndnxt);
111236396Ssklower		}
111336396Ssklower		if(trick_hc)
111436396Ssklower			return error;
111536396Ssklower	}
111636396Ssklower;
111736396Ssklower
111836396Ssklower/* TP4, faked ack in TP0 when cons send completes */
111936396SsklowerSAME 			<==		TP_OPEN 									AK_TPDU
112036396Ssklower	( tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq)  )
112136396Ssklower
112236396Ssklower	/* tp_goodack == true means
112336396Ssklower	 * EITHER it actually acked something heretofore unacknowledged
112436396Ssklower	 * OR no news but the credit should be processed.
112536396Ssklower	 */
112636396Ssklower	{
112750904Ssklower		struct sockbuf *sb = &$P.tp_sock->so_snd;
112850904Ssklower
112936396Ssklower		IFDEBUG(D_ACKRECV)
113036396Ssklower			printf("GOOD ACK seq 0x%x cdt 0x%x\n", $$.e_seq, $$.e_cdt);
113136396Ssklower		ENDDEBUG
113236396Ssklower		if( $P.tp_class != TP_CLASS_0) {
113351204Ssklower			tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
113436396Ssklower		}
113550904Ssklower		sbwakeup(sb);
113636396Ssklower		IFDEBUG(D_ACKRECV)
113751204Ssklower			printf("GOOD ACK new sndnxt 0x%x\n", $P.tp_sndnxt);
113836396Ssklower		ENDDEBUG
113936396Ssklower	}
114036396Ssklower;
114136396Ssklower
114236396Ssklower/* TP4, and TP0 after sending a CC or possibly a CR */
114336396SsklowerSAME			<==		TP_OPEN 			 						 AK_TPDU
114436396Ssklower	DEFAULT
114536396Ssklower	{
114636396Ssklower		IFTRACE(D_ACKRECV)
114736396Ssklower			tptrace(TPPTmisc, "BOGUS ACK fcc_present, tp_r_subseq e_subseq",
114836396Ssklower				$$.e_fcc_present, $P.tp_r_subseq, $$.e_subseq, 0);
114936396Ssklower		ENDTRACE
115036396Ssklower		if( $P.tp_class != TP_CLASS_0 ) {
115136396Ssklower
115236396Ssklower			if ( !$$.e_fcc_present ) {
115336396Ssklower				/* send ACK with FCC */
115436396Ssklower				IncStat( ts_ackreason[_ACK_FCC_] );
115536396Ssklower				(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 1, MNULL);
115636396Ssklower			}
115751204Ssklower			tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
115836396Ssklower		}
115936396Ssklower	}
116036396Ssklower;
116136396Ssklower
116236396Ssklower/* NBS(47) */
116336396Ssklower	/* goes in at *** */
116436396Ssklower		/* just so happens that this is never true now, because we allow
116536396Ssklower		 * only 1 packet in the queue at once (this could be changed)
116636396Ssklower		if	( $P.tp_Xsnd.sb_mb )  {
116736396Ssklower			struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, ??);
116836396Ssklower
116936396Ssklower			(void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
117036396Ssklower			$P.tp_retrans = $P.tp_Nretrans;
117151204Ssklower			tp_ctimeout($P, TM_retrans, (int)$P.tp_xpd_ticks);
117236396Ssklower			SEQ_INC($P, $P.tp_Xsndnxt);
117336396Ssklower		}
117436396Ssklower		 */
117536396Ssklower	/* end of the above hack */
117636396Ssklower
117736396Ssklower/* TP4 only */
117842944SsklowerSAME			<== 	TP_OPEN										XAK_TPDU
117936396Ssklower	( tp_goodXack($P, $$.e_seq) )
118036396Ssklower	/* tp_goodXack checks for good ack, removes the correct
118136396Ssklower	 * tpdu from the queue and  returns 1 if ack was legit, 0 if not.
118236396Ssklower	 * also updates tp_Xuna
118336396Ssklower	 */
118436396Ssklower	{
118551204Ssklower		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
118651204Ssklower		tp_cuntimeout($P, TM_retrans);
118736396Ssklower
118836396Ssklower		sbwakeup( &$P.tp_sock->so_snd );
118936396Ssklower
119036396Ssklower		/* resume normal data */
119136396Ssklower		tp_send($P);
119236396Ssklower	}
119336396Ssklower;
119436396Ssklower
119542944Ssklower/* TP4, and TP0 after sending a CC or possibly a CR */
119642944SsklowerSAME			<==		TP_OPEN 			 						XAK_TPDU
119742944Ssklower	DEFAULT
119842944Ssklower	{
119942944Ssklower		IFTRACE(D_ACKRECV)
120042944Ssklower			tptrace(TPPTmisc, "BOGUS XACK eventtype ", $E.ev_number, 0, 0,0);
120142944Ssklower		ENDTRACE
120242944Ssklower		if( $P.tp_class != TP_CLASS_0 ) {
120351204Ssklower			tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
120442944Ssklower		}
120542944Ssklower	}
120642944Ssklower;
120742944Ssklower
120836396Ssklower/* TP4 only */
120936396SsklowerSAME			<==		TP_OPEN 								TM_sendack
121036396Ssklower	DEFAULT
121136396Ssklower	{
121251204Ssklower		int timo;
121336396Ssklower		IFTRACE(D_TIMER)
121436396Ssklower			tptrace(TPPTsendack, -1, $P.tp_lcredit, $P.tp_sent_uwe,
121536396Ssklower			$P.tp_sent_lcdt, 0);
121636396Ssklower		ENDTRACE
121736396Ssklower		IncPStat($P, tps_n_TMsendack);
121836396Ssklower		(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
121936396Ssklower	}
122036396Ssklower;
122136396Ssklower
122236396Ssklower/* TP0 only */
122336396SsklowerSAME			<==		TP_OPEN 									T_USR_rcvd
122436396Ssklower	($P.tp_class == TP_CLASS_0)
122547281Ssklower	{
122647281Ssklower		if (sbspace(&$P.tp_sock->so_rcv) > 0)
122747281Ssklower			tp0_openflow($P);
122847281Ssklower	}
122936396Ssklower;
123036396Ssklower
123136396Ssklower/* TP4 only */
123236396Ssklower		/* If old credit was zero,
123336396Ssklower		 * we'd better inform other side that we now have space
123436396Ssklower		 * But this is not enough.  Sender might not yet have
123536396Ssklower		 * seen an ack with cdt 0 but it might still think the
123636396Ssklower		 * window is closed, so it's going to wait.
123736396Ssklower		 * Best to send an ack each time.
123836396Ssklower		 * Strictly speaking, this ought to be a function of the
123936396Ssklower		 * general ack strategy.
124036396Ssklower		 */
124136396SsklowerSAME			<==		TP_OPEN 									T_USR_rcvd
124236396Ssklower	DEFAULT
124336396Ssklower	{
124436396Ssklower		if( trick_hc ) {
124551024Ssklower			SeqNum ack_thresh;
124650849Ssklower			/*
124751024Ssklower			 * If the upper window edge has advanced a reasonable
124851024Ssklower			 * amount beyond what was known, send an ACK.
124951024Ssklower			 * A reasonable amount is 2 packets, unless the max window
125051024Ssklower			 * is only 1 or 2 packets, in which case we
125151024Ssklower			 * should send an ack for any advance in the upper window edge.
125250849Ssklower			 */
125351024Ssklower			LOCAL_CREDIT($P);
125451024Ssklower			ack_thresh = SEQ_SUB($P, $P.tp_lcredit + $P.tp_rcvnxt,
125551024Ssklower									 ($P.tp_maxlcredit > 2 ? 2 : 1));
125651024Ssklower			if (SEQ_GT($P, ack_thresh, $P.tp_sent_uwe)) {
125750849Ssklower				IncStat(ts_ackreason[_ACK_USRRCV_]);
125851024Ssklower				$P.tp_flags &= ~TPF_DELACK;
125939921Ssklower				return tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
126050849Ssklower			}
126136396Ssklower		}
126236396Ssklower	}
126336396Ssklower;
126436396Ssklower
126536396Ssklower/* applicable in TP4, TP0 */
126636396SsklowerSAME			<==		TP_REFWAIT 				[ T_USR_rcvd, T_USR_Xrcvd ]
126736396Ssklower	DEFAULT
126836396Ssklower	/* This happens if other end sent a DR when  the user was waiting
126936396Ssklower	 * on a receive.
127036396Ssklower	 * Processing the DR includes putting us in REFWAIT state.
127136396Ssklower	 */
127236396Ssklower	{
127336396Ssklower		if(trick_hc)
127436396Ssklower		return ECONNABORTED;
127536396Ssklower	}
127636396Ssklower;
127736396Ssklower
127836396Ssklower/* TP0 only */
127936396SsklowerTP_REFWAIT		<==		[ TP_OPEN, TP_CRSENT, TP_LISTENING ] 	T_NETRESET
128036396Ssklower	( $P.tp_class != TP_CLASS_4 )
128136396Ssklower		/* 0 or (4 and 0) */
128236396Ssklower		/* in OPEN class will be 0 or 4 but not both */
128336396Ssklower		/* in CRSENT or LISTENING it could be in negotiation, hence both */
128436396Ssklower		/* Actually, this shouldn't ever happen in LISTENING */
128536396Ssklower	{
128636396Ssklower		ASSERT( $P.tp_state != TP_LISTENING );
128736396Ssklower		tp_indicate(T_DISCONNECT, $P, ECONNRESET);
128836396Ssklower		tp_soisdisconnected($P);
128936396Ssklower	}
129036396Ssklower;
129136396Ssklower
129236396Ssklower/* TP4: ignore resets */
129336396SsklowerSAME		<==		[ TP_OPEN, TP_CRSENT, TP_AKWAIT,
129436396Ssklower						TP_CLOSING, TP_LISTENING ] 				T_NETRESET
129536396Ssklower	DEFAULT
129636396Ssklower	NULLACTION
129736396Ssklower;
129836396Ssklower
129936396Ssklower/* applicable in TP4, TP0 */
130036396SsklowerSAME			<==		[ TP_CLOSED, TP_REFWAIT ]				T_NETRESET
130136396Ssklower	DEFAULT
130236396Ssklower	NULLACTION
130336396Ssklower;
130436396Ssklower
130536396Ssklower/* C'EST TOUT */
1306