xref: /csrg-svn/sys/netiso/tp.trans (revision 51214)
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*51214Ssklower *	@(#)tp.trans	7.16 (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*51214Ssklower/* @(#)tp.trans	7.16 (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		IFDEBUG(D_CONN)
37036396Ssklower			printf("event: CR_TPDU emit CC failed done " );
37136396Ssklower		ENDDEBUG
37238841Ssklower		soisdisconnected($P.tp_sock);
373*51214Ssklower		tp_recycle_tsuffix($P);
374*51214Ssklower		tp_freeref($P.tp_lref);
37536396Ssklower		tp_detach($P);
37636396Ssklower	}
37736396Ssklower;
37836396Ssklower
37936396Ssklower/* applicable in TP4, TP0 */
38036396SsklowerTP_CRSENT		<==		TP_CLOSED								T_CONN_req
38136396Ssklower	DEFAULT
38236396Ssklower	{
38336396Ssklower		int error;
38436396Ssklower		struct mbuf *data = MNULL;
38536396Ssklower
38636396Ssklower		IFTRACE(D_CONN)
38737469Ssklower			tptrace(TPPTmisc, "T_CONN_req flags ucddata", (int)$P.tp_flags,
38837469Ssklower			$P.tp_ucddata, 0, 0);
38936396Ssklower		ENDTRACE
39037469Ssklower		data =  MCPY($P.tp_ucddata, M_WAIT);
39137469Ssklower		if (data) {
39236396Ssklower			IFDEBUG(D_CONN)
39336396Ssklower				printf("T_CONN_req.trans m_copy cc 0x%x\n",
39437469Ssklower					$P.tp_ucddata);
39537469Ssklower				dump_mbuf(data, "sosnd @ T_CONN_req");
39636396Ssklower			ENDDEBUG
39736396Ssklower		}
39836396Ssklower
39936396Ssklower		if (error = tp_emit(CR_TPDU_type, $P, 0, 0, data) )
40036396Ssklower			return error; /* driver WON'T change state; will return error */
40136396Ssklower
40251204Ssklower		$P.tp_refstate = REF_OPEN; /* has timers */
40336396Ssklower		if($P.tp_class != TP_CLASS_0) {
40436396Ssklower			$P.tp_retrans = $P.tp_Nretrans;
40551204Ssklower			tp_ctimeout($P, TM_retrans, (int)$P.tp_cr_ticks);
40636396Ssklower		}
40736396Ssklower	}
40836396Ssklower;
40936396Ssklower
41036396Ssklower/* applicable in TP4, TP0, but state TP_AKWAIT doesn't apply to TP0 */
41136396SsklowerTP_REFWAIT 		<==		[ TP_CRSENT, TP_AKWAIT, TP_OPEN ] 			DR_TPDU
41236396Ssklower	DEFAULT
41336396Ssklower	{
41448739Ssklower		sbflush(&$P.tp_Xrcv); /* purge non-delivered data data */
41548739Ssklower		if ($$.e_datalen > 0) {
41636396Ssklower			sbappendrecord(&$P.tp_Xrcv, $$.e_data);
41736396Ssklower			$$.e_data = MNULL;
41836396Ssklower		}
41951208Ssklower		if ($P.tp_state == TP_OPEN)
42051208Ssklower			tp_indicate(T_DISCONNECT, $P, 0);
42151208Ssklower		else {
42251208Ssklower			int so_error = ECONNREFUSED;
42351208Ssklower			if ($$.e_reason != (E_TP_NO_SESSION ^ TP_ERROR_MASK) &&
42451208Ssklower			    $$.e_reason != (E_TP_NO_CR_ON_NC ^ TP_ERROR_MASK) &&
42551208Ssklower			    $$.e_reason != (E_TP_REF_OVERFLOW ^ TP_ERROR_MASK))
42651208Ssklower				so_error = ECONNABORTED;
42751208Ssklower			tp_indicate(T_DISCONNECT, $P, so_error);
42851208Ssklower		}
42936396Ssklower		tp_soisdisconnected($P);
43036396Ssklower		if ($P.tp_class != TP_CLASS_0) {
43136396Ssklower			if ($P.tp_state == TP_OPEN ) {
43251204Ssklower				tp_euntimeout($P, TM_data_retrans); /* all */
43351204Ssklower				tp_cuntimeout($P, TM_retrans);
43451204Ssklower				tp_cuntimeout($P, TM_inact);
43551204Ssklower				tp_cuntimeout($P, TM_sendack);
43636396Ssklower			}
43751204Ssklower			tp_cuntimeout($P, TM_retrans);
43836396Ssklower			if( $$.e_sref !=  0 )
43936396Ssklower				(void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL);
44036396Ssklower		}
44136396Ssklower	}
44236396Ssklower;
44336396Ssklower
44436396SsklowerSAME 			<==		TP_CLOSED 									DR_TPDU
44536396Ssklower	DEFAULT
44636396Ssklower	{
44736396Ssklower		if( $$.e_sref != 0 )
44836396Ssklower			(void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL);
44936396Ssklower		/* reference timer already set - reset it to be safe (???) */
45051204Ssklower		tp_euntimeout($P, TM_reference); /* all */
45151204Ssklower		tp_etimeout($P, TM_reference, (int)$P.tp_refer_ticks);
45236396Ssklower	}
45336396Ssklower;
45436396Ssklower
45536396Ssklower/* NBS(34) */
45636396SsklowerTP_REFWAIT 		<==  	TP_CRSENT  									ER_TPDU
45736396Ssklower	DEFAULT
45836396Ssklower	{
45951204Ssklower		tp_cuntimeout($P, TM_retrans);
46048739Ssklower		tp_indicate(ER_TPDU, $P, $$.e_reason);
46136396Ssklower		tp_soisdisconnected($P);
46236396Ssklower	}
46336396Ssklower;
46436396Ssklower
46536396Ssklower/* NBS(27) */
46636396SsklowerTP_REFWAIT		<==		TP_CLOSING									DR_TPDU
46736396Ssklower	DEFAULT
46836396Ssklower	{
46951204Ssklower		tp_cuntimeout($P, TM_retrans);
47036396Ssklower		tp_soisdisconnected($P);
47136396Ssklower	}
47236396Ssklower;
47336396Ssklower/* these two transitions are the same but can't be combined because xebec
47436396Ssklower * can't handle the use of $$.e_reason if they're combined
47536396Ssklower */
47636396Ssklower/* NBS(27) */
47736396SsklowerTP_REFWAIT		<==		TP_CLOSING									ER_TPDU
47836396Ssklower	DEFAULT
47936396Ssklower	{
48048739Ssklower		tp_indicate(ER_TPDU, $P, $$.e_reason);
48151204Ssklower		tp_cuntimeout($P, TM_retrans);
48236396Ssklower		tp_soisdisconnected($P);
48336396Ssklower	}
48436396Ssklower;
48536396Ssklower/* NBS(27) */
48636396SsklowerTP_REFWAIT		<==		TP_CLOSING									DC_TPDU
48736396Ssklower	DEFAULT
48836396Ssklower	{
48951204Ssklower		tp_cuntimeout($P, TM_retrans);
49036396Ssklower		tp_soisdisconnected($P);
49136396Ssklower	}
49236396Ssklower;
49336396Ssklower
49436396Ssklower/* NBS(21) */
49536396SsklowerSAME 			<== 	TP_CLOSED 						[ CC_TPDU, CR_TPDU ]
49636396Ssklower	DEFAULT
49736396Ssklower	{	/* don't ask me why we have to do this - spec says so */
49836396Ssklower		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_NO_SESSION, MNULL);
49936396Ssklower		/* don't bother with retransmissions of the DR */
50036396Ssklower	}
50136396Ssklower;
50236396Ssklower
50336396Ssklower/* NBS(34) */
50436396SsklowerTP_REFWAIT 		<== 	TP_OPEN  				 					ER_TPDU
50536396Ssklower	($P.tp_class == TP_CLASS_0)
50636396Ssklower	{
50736396Ssklower		tp_soisdisconnecting($P.tp_sock);
50848739Ssklower		tp_indicate(ER_TPDU, $P, $$.e_reason);
50936396Ssklower		tp_soisdisconnected($P);
51036396Ssklower		tp_netcmd( $P, CONN_CLOSE );
51136396Ssklower	}
51236396Ssklower;
51336396Ssklower
51436396SsklowerTP_CLOSING 		<== 	[ TP_AKWAIT, TP_OPEN ]  					ER_TPDU
51536396Ssklower	DEFAULT
51636396Ssklower	{
51736396Ssklower		if ($P.tp_state == TP_OPEN) {
51851204Ssklower			tp_euntimeout($P, TM_data_retrans); /* all */
51951204Ssklower			tp_cuntimeout($P, TM_inact);
52051204Ssklower			tp_cuntimeout($P, TM_sendack);
52136396Ssklower		}
52236396Ssklower		tp_soisdisconnecting($P.tp_sock);
52348739Ssklower		tp_indicate(ER_TPDU, $P, $$.e_reason);
52436396Ssklower		$P.tp_retrans = $P.tp_Nretrans;
52551204Ssklower		tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
52636396Ssklower		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_PROTO_ERR, MNULL);
52736396Ssklower	}
52836396Ssklower;
52936396Ssklower/* NBS(6) */
53036396SsklowerTP_OPEN			<==		TP_CRSENT									CC_TPDU
53136396Ssklower	($P.tp_class == TP_CLASS_0)
53236396Ssklower	{
53351204Ssklower		tp_cuntimeout($P, TM_retrans);
53436396Ssklower		IncStat(ts_tp0_conn);
53536396Ssklower		$P.tp_fcredit = 1;
53636396Ssklower		soisconnected($P.tp_sock);
53736396Ssklower	}
53836396Ssklower;
53936396Ssklower
54036396SsklowerTP_OPEN			<==		TP_CRSENT									CC_TPDU
54136396Ssklower	DEFAULT
54236396Ssklower	{
54336396Ssklower		IFDEBUG(D_CONN)
54436396Ssklower			printf("trans: CC_TPDU in CRSENT state flags 0x%x\n",
54536396Ssklower				(int)$P.tp_flags);
54636396Ssklower		ENDDEBUG
54736396Ssklower		IncStat(ts_tp4_conn);
54836396Ssklower		$P.tp_fref = $$.e_sref;
54936396Ssklower		$P.tp_fcredit = $$.e_cdt;
55047281Ssklower		if (($P.tp_rx_strat & TPRX_FASTSTART) && ($$.e_cdt > 0))
55151204Ssklower			$P.tp_cong_win = $$.e_cdt * $P.tp_l_tpdusize;
55236396Ssklower		tp_getoptions($P);
55351204Ssklower		tp_cuntimeout($P, TM_retrans);
55437469Ssklower		if ($P.tp_ucddata) {
55536396Ssklower			IFDEBUG(D_CONN)
55637469Ssklower				printf("dropping user connect data cc 0x%x\n",
55737469Ssklower					$P.tp_ucddata->m_len);
55836396Ssklower			ENDDEBUG
55937469Ssklower			m_freem($P.tp_ucddata);
56037469Ssklower			$P.tp_ucddata = 0;
56136396Ssklower		}
56236396Ssklower		soisconnected($P.tp_sock);
56336396Ssklower		if ($$.e_datalen > 0) {
56436396Ssklower			ASSERT($P.tp_Xrcv.sb_cc == 0); /* should be empty */
56536396Ssklower			sbappendrecord(&$P.tp_Xrcv, $$.e_data);
56636396Ssklower			$$.e_data = MNULL;
56736396Ssklower		}
56836396Ssklower
56936396Ssklower		(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
57051204Ssklower		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
57136396Ssklower	}
57236396Ssklower;
57336396Ssklower
57436396Ssklower/* TP4 only */
57536396SsklowerSAME			<==		TP_CRSENT									TM_retrans
57636396Ssklower	(	$P.tp_retrans > 0 )
57736396Ssklower	{
57836396Ssklower		struct mbuf *data = MNULL;
57936396Ssklower		int error;
58036396Ssklower
58136396Ssklower		IncStat(ts_retrans_cr);
58251204Ssklower		$P.tp_cong_win = 1 * $P.tp_l_tpdusize;
58337469Ssklower		data = MCPY($P.tp_ucddata, M_NOWAIT);
58437469Ssklower		if($P.tp_ucddata) {
58536396Ssklower			IFDEBUG(D_CONN)
58637469Ssklower				printf("TM_retrans.trans m_copy cc 0x%x\n", data);
58737469Ssklower				dump_mbuf($P.tp_ucddata, "sosnd @ TM_retrans");
58836396Ssklower			ENDDEBUG
58936396Ssklower			if( data == MNULL )
59036396Ssklower				return ENOBUFS;
59136396Ssklower		}
59236396Ssklower
59336396Ssklower		$P.tp_retrans --;
59436396Ssklower		if( error = tp_emit(CR_TPDU_type, $P, 0, 0, data) ) {
59536396Ssklower			$P.tp_sock->so_error = error;
59636396Ssklower		}
59751204Ssklower		tp_ctimeout($P, TM_retrans, (int)$P.tp_cr_ticks);
59836396Ssklower	}
59936396Ssklower;
60036396Ssklower
60136396Ssklower/* TP4 only  */
60236396SsklowerTP_REFWAIT		<==		TP_CRSENT									TM_retrans
60336396Ssklower	DEFAULT /* no more CR retransmissions */
60436396Ssklower	{
60536396Ssklower		IncStat(ts_conn_gaveup);
60636396Ssklower		$P.tp_sock->so_error = ETIMEDOUT;
60736396Ssklower		tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
60836396Ssklower		tp_soisdisconnected($P);
60936396Ssklower	}
61036396Ssklower;
61136396Ssklower
61236396Ssklower/* TP4 only */
61336396SsklowerSAME 			<==	 TP_AKWAIT											CR_TPDU
61436396Ssklower	DEFAULT
61536396Ssklower	/* duplicate CR (which doesn't really exist in the context of
61636396Ssklower	 * a connectionless network layer)
61736396Ssklower	 * Doesn't occur in class 0.
61836396Ssklower	 */
61936396Ssklower	{
62036396Ssklower		int error;
62137469Ssklower		struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
62236396Ssklower
62337469Ssklower		if( error = tp_emit(CC_TPDU_type, $P, 0, 0, data) ) {
62436396Ssklower			$P.tp_sock->so_error = error;
62536396Ssklower		}
62636396Ssklower		$P.tp_retrans = $P.tp_Nretrans;
62751204Ssklower		tp_ctimeout($P, TM_retrans, (int)$P.tp_cc_ticks);
62836396Ssklower	}
62936396Ssklower;
63036396Ssklower
63136396Ssklower/* TP4 only */
63236396SsklowerTP_OPEN			<==		TP_AKWAIT 										DT_TPDU
63336396Ssklower	( IN_RWINDOW( $P, $$.e_seq,
63436396Ssklower					$P.tp_rcvnxt, SEQ($P, $P.tp_rcvnxt + $P.tp_lcredit)) )
63536396Ssklower	{
63636396Ssklower		int doack;
63736396Ssklower
63837469Ssklower		/*
63937469Ssklower		 * Get rid of any confirm or connect data, so that if we
64037469Ssklower		 * crash or close, it isn't thought of as disconnect data.
64137469Ssklower		 */
64237469Ssklower		if ($P.tp_ucddata) {
64337469Ssklower			m_freem($P.tp_ucddata);
64437469Ssklower			$P.tp_ucddata = 0;
64536396Ssklower		}
64651204Ssklower		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
64751204Ssklower		tp_cuntimeout($P, TM_retrans);
64836396Ssklower		soisconnected($P.tp_sock);
64951204Ssklower		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
65036396Ssklower
65136396Ssklower		/* see also next 2 transitions, if you make any changes */
65236396Ssklower
65336396Ssklower		doack = tp_stash($P, $E);
65436396Ssklower		IFDEBUG(D_DATA)
65536396Ssklower			printf("tp_stash returns %d\n",doack);
65636396Ssklower		ENDDEBUG
65736396Ssklower
65851204Ssklower		if (doack) {
65936396Ssklower			(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL );
66051204Ssklower			tp_ctimeout($P, TM_sendack, (int)$P.tp_keepalive_ticks);
66136396Ssklower		} else
66251204Ssklower			tp_ctimeout( $P, TM_sendack, (int)$P.tp_sendack_ticks);
66336396Ssklower
66436396Ssklower		IFDEBUG(D_DATA)
66536396Ssklower			printf("after stash calling sbwakeup\n");
66636396Ssklower		ENDDEBUG
66736396Ssklower	}
66836396Ssklower;
66936396Ssklower
67036396SsklowerSAME			<==		TP_OPEN 									DT_TPDU
67136396Ssklower	( $P.tp_class == TP_CLASS_0 )
67236396Ssklower	{
67336396Ssklower		tp0_stash($P, $E);
67436396Ssklower		sbwakeup( &$P.tp_sock->so_rcv );
67536396Ssklower
67636396Ssklower		IFDEBUG(D_DATA)
67736396Ssklower			printf("after stash calling sbwakeup\n");
67836396Ssklower		ENDDEBUG
67936396Ssklower	}
68036396Ssklower;
68136396Ssklower
68236396Ssklower/* TP4 only */
68336396SsklowerSAME			<==		TP_OPEN 									DT_TPDU
68436396Ssklower	( IN_RWINDOW( $P, $$.e_seq,
68536396Ssklower					$P.tp_rcvnxt, SEQ($P, $P.tp_rcvnxt + $P.tp_lcredit)) )
68636396Ssklower	{
68736396Ssklower		int doack; /* tells if we must ack immediately */
68836396Ssklower
68951204Ssklower		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
69036396Ssklower		sbwakeup( &$P.tp_sock->so_rcv );
69136396Ssklower
69236396Ssklower		doack = tp_stash($P, $E);
69336396Ssklower		IFDEBUG(D_DATA)
69436396Ssklower			printf("tp_stash returns %d\n",doack);
69536396Ssklower		ENDDEBUG
69636396Ssklower
69736396Ssklower		if(doack)
69836396Ssklower			(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL );
69936396Ssklower		else
70051204Ssklower			tp_ctimeout_MIN( $P, TM_sendack, (int)$P.tp_sendack_ticks);
70136396Ssklower
70236396Ssklower		IFDEBUG(D_DATA)
70336396Ssklower			printf("after stash calling sbwakeup\n");
70436396Ssklower		ENDDEBUG
70536396Ssklower	}
70636396Ssklower;
70736396Ssklower
70836396Ssklower/* Not in window  - we must ack under certain circumstances, namely
70936396Ssklower * a) if the seq number is below lwe but > lwe - (max credit ever given)
71036396Ssklower * (to handle lost acks) Can use max-possible-credit for this ^^^.
71136396Ssklower * and
71236396Ssklower * b) seq number is > uwe but < uwe + previously sent & withdrawn credit
71336396Ssklower *
71436396Ssklower * (see 12.2.3.8.1 of ISO spec, p. 73)
71536396Ssklower * We just always ack.
71636396Ssklower */
71736396Ssklower/* TP4 only */
71836396SsklowerSAME 			<== 	[ TP_OPEN, TP_AKWAIT ]							DT_TPDU
71936396Ssklower	DEFAULT /* Not in window */
72036396Ssklower	{
72136396Ssklower		IFTRACE(D_DATA)
72236396Ssklower			tptrace(TPPTmisc, "NIW seq rcvnxt lcredit ",
72336396Ssklower				$$.e_seq, $P.tp_rcvnxt, $P.tp_lcredit, 0);
72436396Ssklower		ENDTRACE
72536396Ssklower		IncStat(ts_dt_niw);
72636396Ssklower		m_freem($$.e_data);
72751204Ssklower		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
72836396Ssklower		(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL );
72936396Ssklower	}
73036396Ssklower;
73136396Ssklower
73236396Ssklower/* TP4 only */
73336396SsklowerTP_OPEN			<==		TP_AKWAIT										AK_TPDU
73436396Ssklower	DEFAULT
73536396Ssklower	{
73637469Ssklower		if ($P.tp_ucddata) {
73737469Ssklower			m_freem($P.tp_ucddata);
73837469Ssklower			$P.tp_ucddata = 0;
73936396Ssklower		}
74036396Ssklower		(void) tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq);
74151204Ssklower		tp_cuntimeout($P, TM_retrans);
74236396Ssklower
74336396Ssklower		soisconnected($P.tp_sock);
74436396Ssklower		IFTRACE(D_CONN)
74536396Ssklower			struct socket *so = $P.tp_sock;
74636396Ssklower			tptrace(TPPTmisc,
74736396Ssklower			"called sosiconn: so so_state rcv.sb_sel rcv.sb_flags",
74836396Ssklower				so, so->so_state, so->so_rcv.sb_sel, so->so_rcv.sb_flags);
74936396Ssklower			tptrace(TPPTmisc,
75036396Ssklower			"called sosiconn 2: so_qlen so_error so_rcv.sb_cc so_head",
75136396Ssklower				so->so_qlen, so->so_error, so->so_rcv.sb_cc, so->so_head);
75236396Ssklower		ENDTRACE
75336396Ssklower
75451204Ssklower		tp_ctimeout($P, TM_sendack, (int)$P.tp_keepalive_ticks);
75551204Ssklower		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
75636396Ssklower	}
75736396Ssklower;
75836396Ssklower
75936396Ssklower/* TP4 only */
76036396SsklowerTP_OPEN 		<== 	[ TP_OPEN, TP_AKWAIT ]						XPD_TPDU
76147281Ssklower	($P.tp_Xrcvnxt == $$.e_seq)
76236396Ssklower	{
76336396Ssklower		if( $P.tp_state == TP_AKWAIT ) {
76437469Ssklower			if ($P.tp_ucddata) {
76537469Ssklower				m_freem($P.tp_ucddata);
76637469Ssklower				$P.tp_ucddata = 0;
76736396Ssklower			}
76851204Ssklower			tp_cuntimeout($P, TM_retrans);
76936396Ssklower			soisconnected($P.tp_sock);
77051204Ssklower			tp_ctimeout($P, TM_sendack, (int)$P.tp_keepalive_ticks);
77151204Ssklower			tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
77236396Ssklower		}
77336396Ssklower		IFTRACE(D_XPD)
77436396Ssklower		tptrace(TPPTmisc, "XPD tpdu accepted Xrcvnxt, e_seq datalen m_len\n",
77536396Ssklower				$P.tp_Xrcvnxt,$$.e_seq,  $$.e_datalen, $$.e_data->m_len);
77636396Ssklower		ENDTRACE
77736396Ssklower
77837469Ssklower		$P.tp_sock->so_state |= SS_RCVATMARK;
77947281Ssklower		$$.e_data->m_flags |= M_EOR;
78037469Ssklower		sbinsertoob(&$P.tp_Xrcv, $$.e_data);
78136396Ssklower		IFDEBUG(D_XPD)
78236396Ssklower			dump_mbuf($$.e_data, "XPD TPDU: tp_Xrcv");
78336396Ssklower		ENDDEBUG
78436396Ssklower		tp_indicate(T_XDATA, $P, 0);
78536396Ssklower		sbwakeup( &$P.tp_Xrcv );
78636396Ssklower
78736396Ssklower		(void) tp_emit(XAK_TPDU_type, $P, $P.tp_Xrcvnxt, 0, MNULL);
78836396Ssklower		SEQ_INC($P, $P.tp_Xrcvnxt);
78936396Ssklower	}
79036396Ssklower;
79136396Ssklower
79236396Ssklower/* TP4 only */
79336396SsklowerSAME			<==		TP_OPEN 									T_USR_Xrcvd
79436396Ssklower	DEFAULT
79536396Ssklower	{
79636396Ssklower		if( $P.tp_Xrcv.sb_cc == 0 ) {
79736396Ssklower			/* kludge for select(): */
79837469Ssklower			/* $P.tp_sock->so_state &= ~SS_OOBAVAIL; */
79936396Ssklower		}
80036396Ssklower	}
80136396Ssklower	/* OLD WAY:
80236396Ssklower	 * Ack only after the user receives the XPD.  This is better for
80336396Ssklower	 * users that use one XPD right after another.
80436396Ssklower	 * Acking right away (the NEW WAY, see the prev. transition) is
80536396Ssklower	 * better for occasional * XPD, when the receiving user doesn't
80636396Ssklower	 * want to read the XPD immediately (which is session's behavior).
80736396Ssklower	 *
80836396Ssklower		int error = tp_emit(XAK_TPDU_type, $P, $P.tp_Xrcvnxt, 0, MNULL);
80936396Ssklower		SEQ_INC($P, $P.tp_Xrcvnxt);
81036396Ssklower		return error;
81136396Ssklower	*/
81236396Ssklower;
81336396Ssklower
81436396Ssklower/* NOTE: presently if the user doesn't read the connection data
81536396Ssklower * before and expedited data PDU comes in, the connection data will
81636396Ssklower * be dropped. This is a bug.  To avoid it, we need somewhere else
81736396Ssklower * to put the connection data.
81836396Ssklower * On the other hand, we need not to have it sitting around forever.
81936396Ssklower * This is a problem with the idea of trying to accommodate
82036396Ssklower * data on connect w/ a passive-open user interface.
82136396Ssklower */
82236396Ssklower/* TP4 only */
82336396Ssklower
82436396SsklowerSAME	 		<== 	[ TP_AKWAIT, TP_OPEN ] 							XPD_TPDU
82536396Ssklower	DEFAULT /* not in window or cdt==0 */
82636396Ssklower	{
82736396Ssklower		IFTRACE(D_XPD)
82836396Ssklower			tptrace(TPPTmisc, "XPD tpdu niw (Xrcvnxt, e_seq) or not cdt (cc)\n",
82936396Ssklower				$P.tp_Xrcvnxt, $$.e_seq,  $P.tp_Xrcv.sb_cc , 0);
83036396Ssklower		ENDTRACE
83136396Ssklower		if( $P.tp_Xrcvnxt != $$.e_seq )
83236396Ssklower			IncStat(ts_xpd_niw);
83336396Ssklower		if( $P.tp_Xrcv.sb_cc ) {
83436396Ssklower			/* might as well kick 'em again */
83536396Ssklower			tp_indicate(T_XDATA, $P, 0);
83636396Ssklower			IncStat(ts_xpd_dup);
83736396Ssklower		}
83836396Ssklower		m_freem($$.e_data);
83951204Ssklower		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
84036396Ssklower		/* don't send an xack because the xak gives "last one received", not
84136396Ssklower		 * "next one i expect" (dumb)
84236396Ssklower		 */
84336396Ssklower	}
84436396Ssklower;
84536396Ssklower
84636396Ssklower/* Occurs (AKWAIT, OPEN) when parent (listening) socket gets aborted, and tries
84736396Ssklower * to detach all its "children"
84836396Ssklower * Also (CRSENT) when user kills a job that's doing a connect()
84936396Ssklower */
85036396SsklowerTP_REFWAIT		<== 	TP_CRSENT 										T_DETACH
85136396Ssklower	($P.tp_class == TP_CLASS_0)
85236396Ssklower	{
85336396Ssklower		struct socket *so = $P.tp_sock;
85436396Ssklower
85536396Ssklower		/* detach from parent socket so it can finish closing */
85636396Ssklower		if (so->so_head) {
85736396Ssklower			if (!soqremque(so, 0) && !soqremque(so, 1))
85836396Ssklower				panic("tp: T_DETACH");
85936396Ssklower			so->so_head = 0;
86036396Ssklower		}
86136396Ssklower		tp_soisdisconnecting($P.tp_sock);
86236396Ssklower		tp_netcmd( $P, CONN_CLOSE);
86336396Ssklower		tp_soisdisconnected($P);
86436396Ssklower	}
86536396Ssklower;
86636396Ssklower
86736396Ssklower/* TP4 only */
86838841SsklowerTP_CLOSING		<== [ TP_CLOSING, TP_AKWAIT, TP_CRSENT, TP_CONFIRMING ]	T_DETACH
86936396Ssklower	DEFAULT
87036396Ssklower	{
87136396Ssklower		struct socket *so = $P.tp_sock;
87237469Ssklower		struct mbuf *data = MNULL;
87336396Ssklower
87436396Ssklower		/* detach from parent socket so it can finish closing */
87536396Ssklower		if (so->so_head) {
87636396Ssklower			if (!soqremque(so, 0) && !soqremque(so, 1))
87736396Ssklower				panic("tp: T_DETACH");
87836396Ssklower			so->so_head = 0;
87936396Ssklower		}
88036396Ssklower		if ($P.tp_state != TP_CLOSING) {
88136396Ssklower			tp_soisdisconnecting($P.tp_sock);
88237469Ssklower			data = MCPY($P.tp_ucddata, M_NOWAIT);
88337469Ssklower			(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_NORMAL_DISC, data);
88436396Ssklower			$P.tp_retrans = $P.tp_Nretrans;
88551204Ssklower			tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
88636396Ssklower		}
88736396Ssklower	}
88836396Ssklower;
88936396Ssklower
89036396SsklowerTP_REFWAIT		<==		[ TP_OPEN, TP_CRSENT ]		 	  			T_DISC_req
89136396Ssklower	( $P.tp_class == TP_CLASS_0 )
89236396Ssklower	{
89336396Ssklower		tp_soisdisconnecting($P.tp_sock);
89436396Ssklower		tp_netcmd( $P, CONN_CLOSE);
89536396Ssklower		tp_soisdisconnected($P);
89636396Ssklower	}
89736396Ssklower;
89836396Ssklower
89936396Ssklower/* TP4 only */
90038841SsklowerTP_CLOSING		<==	[ TP_AKWAIT, TP_OPEN, TP_CRSENT, TP_CONFIRMING ]  T_DISC_req
90136396Ssklower	DEFAULT
90236396Ssklower	{
90337469Ssklower		struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
90436396Ssklower
90536396Ssklower		if($P.tp_state == TP_OPEN) {
90651204Ssklower			tp_euntimeout($P, TM_data_retrans); /* all */
90751204Ssklower			tp_cuntimeout($P, TM_inact);
90851204Ssklower			tp_cuntimeout($P, TM_sendack);
90936396Ssklower		}
91037469Ssklower		if (data) {
91136396Ssklower			IFDEBUG(D_CONN)
91237469Ssklower				printf("T_DISC_req.trans tp_ucddata 0x%x\n",
91337469Ssklower					$P.tp_ucddata);
91437469Ssklower				dump_mbuf(data, "ucddata @ T_DISC_req");
91536396Ssklower			ENDDEBUG
91636396Ssklower		}
91736396Ssklower		tp_soisdisconnecting($P.tp_sock);
91836396Ssklower		$P.tp_retrans = $P.tp_Nretrans;
91951204Ssklower		tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
92036396Ssklower
92136396Ssklower		if( trick_hc )
92236396Ssklower			return tp_emit(DR_TPDU_type, $P, 0, $$.e_reason, data);
92336396Ssklower	}
92436396Ssklower;
92536396Ssklower
92636396Ssklower/* TP4 only */
92736396SsklowerSAME			<==		TP_AKWAIT									TM_retrans
92836396Ssklower	( $P.tp_retrans > 0 )
92936396Ssklower	{
93036396Ssklower		int error;
93137469Ssklower		struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
93236396Ssklower
93336396Ssklower		IncStat(ts_retrans_cc);
93436396Ssklower		$P.tp_retrans --;
93551204Ssklower		$P.tp_cong_win = 1 * $P.tp_l_tpdusize;
93639921Ssklower
93737469Ssklower		if( error = tp_emit(CC_TPDU_type, $P, 0, 0, data) )
93836396Ssklower			$P.tp_sock->so_error = error;
93951204Ssklower		tp_ctimeout($P, TM_retrans, (int)$P.tp_cc_ticks);
94036396Ssklower	}
94136396Ssklower;
94236396Ssklower
94336396Ssklower/* TP4 only */
94436396SsklowerTP_CLOSING		<==		TP_AKWAIT									TM_retrans
94536396Ssklower	DEFAULT  /* out of time */
94636396Ssklower	{
94736396Ssklower		IncStat(ts_conn_gaveup);
94836396Ssklower		tp_soisdisconnecting($P.tp_sock);
94936396Ssklower		$P.tp_sock->so_error = ETIMEDOUT;
95036396Ssklower		tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
95136396Ssklower		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_CONGEST, MNULL);
95236396Ssklower		$P.tp_retrans = $P.tp_Nretrans;
95351204Ssklower		tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
95436396Ssklower	}
95536396Ssklower;
95636396Ssklower
95736396Ssklower/* the retrans timers had better go off BEFORE the inactivity timer does,
95836396Ssklower * if transmissions are going on.
95936396Ssklower * (i.e., TM_inact should be greater than timer for all retrans plus ack
96036396Ssklower * turnaround)
96136396Ssklower */
96236396Ssklower/* TP4 only */
96336396SsklowerTP_CLOSING 		<==		TP_OPEN		   [ TM_inact, TM_retrans, TM_data_retrans ]
96436396Ssklower	DEFAULT
96536396Ssklower	{
96651204Ssklower		tp_euntimeout($P, TM_data_retrans); /* all */
96751204Ssklower		tp_cuntimeout($P, TM_inact);
96851204Ssklower		tp_cuntimeout($P, TM_sendack);
96936396Ssklower
97036396Ssklower		IncStat(ts_conn_gaveup);
97136396Ssklower		tp_soisdisconnecting($P.tp_sock);
97236396Ssklower		$P.tp_sock->so_error = ETIMEDOUT;
97336396Ssklower		tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
97436396Ssklower		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_CONGEST_2, MNULL);
97536396Ssklower		$P.tp_retrans = $P.tp_Nretrans;
97651204Ssklower		tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
97736396Ssklower	}
97836396Ssklower;
97936396Ssklower
98036396Ssklower/* TP4 only */
98136396SsklowerSAME			<==		TP_OPEN										TM_retrans
98236396Ssklower	( $P.tp_retrans > 0 )
98336396Ssklower	{
98451204Ssklower		$P.tp_cong_win = 1 * $P.tp_l_tpdusize;
98536396Ssklower		/* resume XPD */
98636396Ssklower		if	( $P.tp_Xsnd.sb_mb )  {
98737469Ssklower			struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc);
98851204Ssklower			int shift;
98936396Ssklower
99036396Ssklower			IFTRACE(D_XPD)
99151204Ssklower				tptrace(TPPTmisc, "XPD retrans: Xuna Xsndnxt sndnxt snduna",
99251204Ssklower					$P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndnxt,
99336396Ssklower					$P.tp_snduna);
99436396Ssklower			ENDTRACE
99536396Ssklower			IFDEBUG(D_XPD)
99636396Ssklower				dump_mbuf(m, "XPD retrans emitting M");
99736396Ssklower			ENDDEBUG
99836396Ssklower			IncStat(ts_retrans_xpd);
99936396Ssklower			$P.tp_retrans --;
100051204Ssklower			shift = max($P.tp_Nretrans - $P.tp_retrans, 6);
100136396Ssklower			(void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
100251204Ssklower			tp_ctimeout($P, TM_retrans, ((int)$P.tp_dt_ticks) << shift);
100336396Ssklower		}
100436396Ssklower	}
100536396Ssklower;
100636396Ssklower
100736396Ssklower/* TP4 only */
100836396SsklowerSAME 			<==		TP_OPEN									TM_data_retrans
100951204Ssklower	($P.tp_rxtshift < TP_NRETRANS)
101036396Ssklower	{
101151204Ssklower		$P.tp_rxtshift++;
101251204Ssklower		(void) tp_data_retrans($P);
101336396Ssklower	}
101436396Ssklower;
101536396Ssklower
101636396Ssklower/* TP4 only */
101736396SsklowerSAME	 		<==		TP_CLOSING									TM_retrans
101836396Ssklower	(	$P.tp_retrans > 0 )
101936396Ssklower	{
102036396Ssklower		$P.tp_retrans --;
102136396Ssklower		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_DR_NO_REAS, MNULL);
102236396Ssklower		IncStat(ts_retrans_dr);
102351204Ssklower		tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
102436396Ssklower	}
102536396Ssklower;
102636396Ssklower
102736396Ssklower/* TP4 only */
102836396SsklowerTP_REFWAIT 		<==		TP_CLOSING									TM_retrans
102936396Ssklower	DEFAULT	/* no more retrans - gave up */
103036396Ssklower	{
103136396Ssklower		$P.tp_sock->so_error = ETIMEDOUT;
103251204Ssklower		$P.tp_refstate = REF_FROZEN;
103336396Ssklower		tp_recycle_tsuffix( $P );
103451204Ssklower		tp_etimeout($P, TM_reference, (int)$P.tp_refer_ticks);
103536396Ssklower	}
103636396Ssklower;
103736396Ssklower
103836396Ssklower/*
103936396Ssklower * The resources are kept around until the ref timer goes off.
104036396Ssklower * The suffices are wiped out sooner so they can be reused right away.
104136396Ssklower */
104236396Ssklower/* applicable in TP4, TP0 */
104336396SsklowerTP_CLOSED 		<==		TP_REFWAIT 									TM_reference
104436396Ssklower	DEFAULT
104536396Ssklower	{
1046*51214Ssklower		tp_freeref($P.tp_lref);
104736396Ssklower		tp_detach($P);
104836396Ssklower	}
104936396Ssklower;
105036396Ssklower
105136396Ssklower/* applicable in TP4, TP0 */
105236396Ssklower/* A duplicate CR from connectionless network layer can't happen */
105336396SsklowerSAME 			<== 	TP_OPEN 							[ CR_TPDU, CC_TPDU ]
105436396Ssklower	DEFAULT
105536396Ssklower	{
105636396Ssklower		if( $P.tp_class != TP_CLASS_0) {
105751204Ssklower			tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
105836396Ssklower			if ( $E.ev_number == CC_TPDU )
105936396Ssklower				(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
106036396Ssklower		}
106136396Ssklower		/* ignore it if class 0 - state tables are blank for this */
106236396Ssklower	}
106336396Ssklower;
106436396Ssklower
106536396Ssklower/* applicable in TP4, TP0 */
106636396SsklowerSAME			<== 	TP_OPEN									T_DATA_req
106736396Ssklower	DEFAULT
106836396Ssklower	{
106936396Ssklower		IFTRACE(D_DATA)
107051204Ssklower			tptrace(TPPTmisc, "T_DATA_req sndnxt snduna fcredit, tpcb",
107151204Ssklower				$P.tp_sndnxt, $P.tp_snduna, $P.tp_fcredit, $P);
107236396Ssklower		ENDTRACE
107336396Ssklower
107436396Ssklower		tp_send($P);
107536396Ssklower	}
107636396Ssklower;
107736396Ssklower
107836396Ssklower/* TP4 only */
107936396SsklowerSAME			<==		TP_OPEN										T_XPD_req
108036396Ssklower	DEFAULT
108136396Ssklower		/* T_XPD_req was issued by sosend iff xpd socket buf was empty
108236396Ssklower		 * at time of sosend(),
108336396Ssklower		 * AND (which means) there were no unacknowledged XPD tpdus outstanding!
108436396Ssklower		 */
108536396Ssklower	{
108636396Ssklower		int error = 0;
108736396Ssklower
108836396Ssklower		/* resume XPD */
108936396Ssklower		if	( $P.tp_Xsnd.sb_mb )  {
109037469Ssklower			struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc);
109136396Ssklower			/* m_copy doesn't preserve the m_xlink field, but at this pt.
109236396Ssklower			 * that doesn't matter
109336396Ssklower			 */
109436396Ssklower
109536396Ssklower			IFTRACE(D_XPD)
109651204Ssklower				tptrace(TPPTmisc, "XPD req: Xuna Xsndnxt sndnxt snduna",
109751204Ssklower					$P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndnxt,
109836396Ssklower					$P.tp_snduna);
109936396Ssklower			ENDTRACE
110036396Ssklower			IFDEBUG(D_XPD)
110136396Ssklower				printf("T_XPD_req: sb_cc 0x%x\n", $P.tp_Xsnd.sb_cc);
110236396Ssklower				dump_mbuf(m, "XPD req emitting M");
110336396Ssklower			ENDDEBUG
110436396Ssklower			error =
110536396Ssklower				tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
110636396Ssklower			$P.tp_retrans = $P.tp_Nretrans;
110751204Ssklower
110851204Ssklower			tp_ctimeout($P, TM_retrans, (int)$P.tp_rxtcur);
110936396Ssklower			SEQ_INC($P, $P.tp_Xsndnxt);
111036396Ssklower		}
111136396Ssklower		if(trick_hc)
111236396Ssklower			return error;
111336396Ssklower	}
111436396Ssklower;
111536396Ssklower
111636396Ssklower/* TP4, faked ack in TP0 when cons send completes */
111736396SsklowerSAME 			<==		TP_OPEN 									AK_TPDU
111836396Ssklower	( tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq)  )
111936396Ssklower
112036396Ssklower	/* tp_goodack == true means
112136396Ssklower	 * EITHER it actually acked something heretofore unacknowledged
112236396Ssklower	 * OR no news but the credit should be processed.
112336396Ssklower	 */
112436396Ssklower	{
112550904Ssklower		struct sockbuf *sb = &$P.tp_sock->so_snd;
112650904Ssklower
112736396Ssklower		IFDEBUG(D_ACKRECV)
112836396Ssklower			printf("GOOD ACK seq 0x%x cdt 0x%x\n", $$.e_seq, $$.e_cdt);
112936396Ssklower		ENDDEBUG
113036396Ssklower		if( $P.tp_class != TP_CLASS_0) {
113151204Ssklower			tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
113236396Ssklower		}
113350904Ssklower		sbwakeup(sb);
113436396Ssklower		IFDEBUG(D_ACKRECV)
113551204Ssklower			printf("GOOD ACK new sndnxt 0x%x\n", $P.tp_sndnxt);
113636396Ssklower		ENDDEBUG
113736396Ssklower	}
113836396Ssklower;
113936396Ssklower
114036396Ssklower/* TP4, and TP0 after sending a CC or possibly a CR */
114136396SsklowerSAME			<==		TP_OPEN 			 						 AK_TPDU
114236396Ssklower	DEFAULT
114336396Ssklower	{
114436396Ssklower		IFTRACE(D_ACKRECV)
114536396Ssklower			tptrace(TPPTmisc, "BOGUS ACK fcc_present, tp_r_subseq e_subseq",
114636396Ssklower				$$.e_fcc_present, $P.tp_r_subseq, $$.e_subseq, 0);
114736396Ssklower		ENDTRACE
114836396Ssklower		if( $P.tp_class != TP_CLASS_0 ) {
114936396Ssklower
115036396Ssklower			if ( !$$.e_fcc_present ) {
115136396Ssklower				/* send ACK with FCC */
115236396Ssklower				IncStat( ts_ackreason[_ACK_FCC_] );
115336396Ssklower				(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 1, MNULL);
115436396Ssklower			}
115551204Ssklower			tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
115636396Ssklower		}
115736396Ssklower	}
115836396Ssklower;
115936396Ssklower
116036396Ssklower/* NBS(47) */
116136396Ssklower	/* goes in at *** */
116236396Ssklower		/* just so happens that this is never true now, because we allow
116336396Ssklower		 * only 1 packet in the queue at once (this could be changed)
116436396Ssklower		if	( $P.tp_Xsnd.sb_mb )  {
116536396Ssklower			struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, ??);
116636396Ssklower
116736396Ssklower			(void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
116836396Ssklower			$P.tp_retrans = $P.tp_Nretrans;
116951204Ssklower			tp_ctimeout($P, TM_retrans, (int)$P.tp_xpd_ticks);
117036396Ssklower			SEQ_INC($P, $P.tp_Xsndnxt);
117136396Ssklower		}
117236396Ssklower		 */
117336396Ssklower	/* end of the above hack */
117436396Ssklower
117536396Ssklower/* TP4 only */
117642944SsklowerSAME			<== 	TP_OPEN										XAK_TPDU
117736396Ssklower	( tp_goodXack($P, $$.e_seq) )
117836396Ssklower	/* tp_goodXack checks for good ack, removes the correct
117936396Ssklower	 * tpdu from the queue and  returns 1 if ack was legit, 0 if not.
118036396Ssklower	 * also updates tp_Xuna
118136396Ssklower	 */
118236396Ssklower	{
118351204Ssklower		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
118451204Ssklower		tp_cuntimeout($P, TM_retrans);
118536396Ssklower
118636396Ssklower		sbwakeup( &$P.tp_sock->so_snd );
118736396Ssklower
118836396Ssklower		/* resume normal data */
118936396Ssklower		tp_send($P);
119036396Ssklower	}
119136396Ssklower;
119236396Ssklower
119342944Ssklower/* TP4, and TP0 after sending a CC or possibly a CR */
119442944SsklowerSAME			<==		TP_OPEN 			 						XAK_TPDU
119542944Ssklower	DEFAULT
119642944Ssklower	{
119742944Ssklower		IFTRACE(D_ACKRECV)
119842944Ssklower			tptrace(TPPTmisc, "BOGUS XACK eventtype ", $E.ev_number, 0, 0,0);
119942944Ssklower		ENDTRACE
120042944Ssklower		if( $P.tp_class != TP_CLASS_0 ) {
120151204Ssklower			tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
120242944Ssklower		}
120342944Ssklower	}
120442944Ssklower;
120542944Ssklower
120636396Ssklower/* TP4 only */
120736396SsklowerSAME			<==		TP_OPEN 								TM_sendack
120836396Ssklower	DEFAULT
120936396Ssklower	{
121051204Ssklower		int timo;
121136396Ssklower		IFTRACE(D_TIMER)
121236396Ssklower			tptrace(TPPTsendack, -1, $P.tp_lcredit, $P.tp_sent_uwe,
121336396Ssklower			$P.tp_sent_lcdt, 0);
121436396Ssklower		ENDTRACE
121536396Ssklower		IncPStat($P, tps_n_TMsendack);
121636396Ssklower		(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
121736396Ssklower	}
121836396Ssklower;
121936396Ssklower
122036396Ssklower/* TP0 only */
122136396SsklowerSAME			<==		TP_OPEN 									T_USR_rcvd
122236396Ssklower	($P.tp_class == TP_CLASS_0)
122347281Ssklower	{
122447281Ssklower		if (sbspace(&$P.tp_sock->so_rcv) > 0)
122547281Ssklower			tp0_openflow($P);
122647281Ssklower	}
122736396Ssklower;
122836396Ssklower
122936396Ssklower/* TP4 only */
123036396Ssklower		/* If old credit was zero,
123136396Ssklower		 * we'd better inform other side that we now have space
123236396Ssklower		 * But this is not enough.  Sender might not yet have
123336396Ssklower		 * seen an ack with cdt 0 but it might still think the
123436396Ssklower		 * window is closed, so it's going to wait.
123536396Ssklower		 * Best to send an ack each time.
123636396Ssklower		 * Strictly speaking, this ought to be a function of the
123736396Ssklower		 * general ack strategy.
123836396Ssklower		 */
123936396SsklowerSAME			<==		TP_OPEN 									T_USR_rcvd
124036396Ssklower	DEFAULT
124136396Ssklower	{
124236396Ssklower		if( trick_hc ) {
124351024Ssklower			SeqNum ack_thresh;
124450849Ssklower			/*
124551024Ssklower			 * If the upper window edge has advanced a reasonable
124651024Ssklower			 * amount beyond what was known, send an ACK.
124751024Ssklower			 * A reasonable amount is 2 packets, unless the max window
124851024Ssklower			 * is only 1 or 2 packets, in which case we
124951024Ssklower			 * should send an ack for any advance in the upper window edge.
125050849Ssklower			 */
125151024Ssklower			LOCAL_CREDIT($P);
125251024Ssklower			ack_thresh = SEQ_SUB($P, $P.tp_lcredit + $P.tp_rcvnxt,
125351024Ssklower									 ($P.tp_maxlcredit > 2 ? 2 : 1));
125451024Ssklower			if (SEQ_GT($P, ack_thresh, $P.tp_sent_uwe)) {
125550849Ssklower				IncStat(ts_ackreason[_ACK_USRRCV_]);
125651024Ssklower				$P.tp_flags &= ~TPF_DELACK;
125739921Ssklower				return tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
125850849Ssklower			}
125936396Ssklower		}
126036396Ssklower	}
126136396Ssklower;
126236396Ssklower
126336396Ssklower/* applicable in TP4, TP0 */
126436396SsklowerSAME			<==		TP_REFWAIT 				[ T_USR_rcvd, T_USR_Xrcvd ]
126536396Ssklower	DEFAULT
126636396Ssklower	/* This happens if other end sent a DR when  the user was waiting
126736396Ssklower	 * on a receive.
126836396Ssklower	 * Processing the DR includes putting us in REFWAIT state.
126936396Ssklower	 */
127036396Ssklower	{
127136396Ssklower		if(trick_hc)
127236396Ssklower		return ECONNABORTED;
127336396Ssklower	}
127436396Ssklower;
127536396Ssklower
127636396Ssklower/* TP0 only */
127736396SsklowerTP_REFWAIT		<==		[ TP_OPEN, TP_CRSENT, TP_LISTENING ] 	T_NETRESET
127836396Ssklower	( $P.tp_class != TP_CLASS_4 )
127936396Ssklower		/* 0 or (4 and 0) */
128036396Ssklower		/* in OPEN class will be 0 or 4 but not both */
128136396Ssklower		/* in CRSENT or LISTENING it could be in negotiation, hence both */
128236396Ssklower		/* Actually, this shouldn't ever happen in LISTENING */
128336396Ssklower	{
128436396Ssklower		ASSERT( $P.tp_state != TP_LISTENING );
128536396Ssklower		tp_indicate(T_DISCONNECT, $P, ECONNRESET);
128636396Ssklower		tp_soisdisconnected($P);
128736396Ssklower	}
128836396Ssklower;
128936396Ssklower
129036396Ssklower/* TP4: ignore resets */
129136396SsklowerSAME		<==		[ TP_OPEN, TP_CRSENT, TP_AKWAIT,
129236396Ssklower						TP_CLOSING, TP_LISTENING ] 				T_NETRESET
129336396Ssklower	DEFAULT
129436396Ssklower	NULLACTION
129536396Ssklower;
129636396Ssklower
129736396Ssklower/* applicable in TP4, TP0 */
129836396SsklowerSAME			<==		[ TP_CLOSED, TP_REFWAIT ]				T_NETRESET
129936396Ssklower	DEFAULT
130036396Ssklower	NULLACTION
130136396Ssklower;
130236396Ssklower
130336396Ssklower/* C'EST TOUT */
1304