xref: /csrg-svn/sys/netiso/tp.trans (revision 51208)
1/* NEW */
2/*-
3 * Copyright (c) 1991 The Regents of the University of California.
4 * All rights reserved.
5 *
6 * %sccs.include.redist.c%
7 *
8 *	@(#)tp.trans	7.15 (Berkeley) 09/30/91
9 */
10
11/***********************************************************
12		Copyright IBM Corporation 1987
13
14                      All Rights Reserved
15
16Permission to use, copy, modify, and distribute this software and its
17documentation for any purpose and without fee is hereby granted,
18provided that the above copyright notice appear in all copies and that
19both that copyright notice and this permission notice appear in
20supporting documentation, and that the name of IBM not be
21used in advertising or publicity pertaining to distribution of the
22software without specific, written prior permission.
23
24IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
25ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
26IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
27ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
28WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
29ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30SOFTWARE.
31
32******************************************************************/
33
34/*
35 * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
36 */
37/* $Header: tp.trans,v 5.1 88/10/12 12:22:07 root Exp $
38 *
39 * Transition file for TP.
40 *
41 * DO NOT:
42 * - change the order of any of the events or states.  to do so will
43 *   make tppt, netstat, etc. cease working.
44 *
45 * NOTE:
46 * some hooks exist for data on (dis)connect, but it's ***NOT***SUPPORTED***
47 * (read: may not work!)
48 *
49 * I tried to put everything that causes a change of state in here, hence
50 * there are some seemingly trivial events  like T_DETACH and T_LISTEN_req.
51 *
52 * Almost everything having to do w/ setting & cancelling timers is here
53 * but once it was debugged, I moved the setting of the
54 * keepalive (sendack) timer to tp_emit(), where an AK_TPDU is sent.
55 * This is so the code wouldn't be duplicated all over creation in here.
56 *
57 */
58*PROTOCOL tp
59
60*INCLUDE
61{
62/* @(#)tp.trans	7.15 (Berkeley) 09/30/91 */
63#include "param.h"
64#include "socket.h"
65#include "socketvar.h"
66#include "protosw.h"
67#include "mbuf.h"
68#include "time.h"
69#include "errno.h"
70#include "../netiso/tp_param.h"
71#include "../netiso/tp_stat.h"
72#include "../netiso/tp_pcb.h"
73#include "../netiso/tp_tpdu.h"
74#include "../netiso/argo_debug.h"
75#include "../netiso/tp_trace.h"
76#include "../netiso/iso_errno.h"
77#include "../netiso/tp_seq.h"
78#include "../netiso/cons.h"
79
80#define DRIVERTRACE TPPTdriver
81#define sbwakeup(sb)	sowakeup(p->tp_sock, sb);
82#define MCPY(d, w) (d ? m_copym(d, 0, (int)M_COPYALL, w): 0)
83
84static 	trick_hc = 1;
85
86int 	tp_emit(),
87		tp_goodack(),				tp_goodXack(),
88		tp_stash()
89;
90void	tp_indicate(),				tp_getoptions(),
91		tp_soisdisconnecting(), 	tp_soisdisconnected(),
92		tp_recycle_tsuffix(),
93		tp_etimeout(),				tp_euntimeout(),
94		tp_ctimeout(),
95		tp_cuntimeout(),			tp_ctimeout_MIN(),
96		tp_freeref(),				tp_detach(),
97		tp0_stash(), 				tp0_send(),
98		tp_netcmd(),				tp_send()
99;
100
101typedef  struct tp_pcb tpcb_struct;
102
103
104}
105
106*PCB    tpcb_struct 	SYNONYM  P
107
108*STATES
109
110TP_CLOSED
111TP_CRSENT
112TP_AKWAIT
113TP_OPEN
114TP_CLOSING
115TP_REFWAIT
116TP_LISTENING	/* Local to this implementation */
117TP_CONFIRMING	/* Local to this implementation */
118
119*EVENTS		{ struct timeval e_time; } 		SYNONYM  E
120
121 /*
122  * C (typically cancelled) timers  -
123  *
124  * let these be the first ones so for the sake of convenience
125  * their values are 0--> n-1
126  * DO NOT CHANGE THE ORDER OF THESE TIMER EVENTS!!
127  */
128 TM_inact
129 TM_retrans
130				/* TM_retrans is used for all
131				 * simple retransmissions - CR,CC,XPD,DR
132				 */
133
134 TM_sendack
135				/* TM_sendack does dual duty - keepalive AND closed-window
136				 * Probes.
137				 * It's set w/ keepalive-ticks every time an ack is sent.
138				 * (this is done in (void) tp_emit() ).
139				 * Whenever a DT arrives which doesn't require immediate acking,
140				 * a separate fast-timeout flag is set ensuring 200ms response.
141				 */
142 TM_notused
143
144 /*
145  * E (typically expired) timers - these may be in any order.
146  * These cause procedures to be executed directly; may not
147  * cause an 'event' as we know them here.
148  */
149 TM_reference		{ SeqNum e_low; SeqNum e_high; int e_retrans; }
150 TM_data_retrans	{ SeqNum e_low; SeqNum e_high; int e_retrans; }
151
152/* NOTE: in tp_input is a minor optimization that assumes that
153 * for all tpdu types that can take e_data and e_datalen, these
154 * fields fall in the same place in the event structure, that is,
155 * e_data is the first field and e_datalen is the 2nd field.
156 */
157
158 ER_TPDU  	 	{
159				  u_char		e_reason;
160				}
161 CR_TPDU  	 	{ struct mbuf 	*e_data;	/* first field */
162				  int 			e_datalen; /* 2nd field */
163				  u_int			e_cdt;
164				}
165 DR_TPDU   	 	{ struct mbuf 	*e_data;	/* first field */
166				  int 			e_datalen; /* 2nd field */
167				  u_short		e_sref;
168				  u_char		e_reason;
169				}
170 DC_TPDU
171 CC_TPDU   	 	{ struct mbuf 	*e_data;	/* first field */
172				  int 			e_datalen; /* 2nd field */
173				  u_short		e_sref;
174				  u_int			e_cdt;
175				}
176 AK_TPDU		{ u_int			e_cdt;
177				  SeqNum 	 	e_seq;
178				  SeqNum 	 	e_subseq;
179				  u_char 	 	e_fcc_present;
180				}
181 DT_TPDU		{ struct mbuf	*e_data; 	/* first field */
182				  int 			e_datalen; /* 2nd field */
183				  u_int 		e_eot;
184				  SeqNum		e_seq;
185				}
186 XPD_TPDU		{ struct mbuf 	*e_data;	/* first field */
187				  int 			e_datalen; 	/* 2nd field */
188				  SeqNum 		e_seq;
189				}
190 XAK_TPDU		{ SeqNum 		e_seq;		}
191
192 T_CONN_req
193 T_DISC_req		{ u_char		e_reason; 	}
194 T_LISTEN_req
195 T_DATA_req
196 T_XPD_req
197 T_USR_rcvd
198 T_USR_Xrcvd
199 T_DETACH
200 T_NETRESET
201 T_ACPT_req
202
203
204*TRANSITIONS
205
206
207/* TP_AKWAIT doesn't exist in TP 0 */
208SAME			<==			TP_AKWAIT			[ CC_TPDU, DC_TPDU, XAK_TPDU ]
209	DEFAULT
210	NULLACTION
211;
212
213
214/* applicable in TP4, TP0 */
215SAME			<==			TP_REFWAIT								DR_TPDU
216	( $$.e_sref !=  0 )
217	{
218		(void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL);
219	}
220;
221
222/* applicable in TP4, TP0 */
223SAME			<==			TP_REFWAIT			[ CR_TPDU, CC_TPDU, DT_TPDU,
224					DR_TPDU, XPD_TPDU, AK_TPDU, XAK_TPDU, DC_TPDU, ER_TPDU ]
225	DEFAULT
226	{
227#		ifdef TP_DEBUG
228		if( $E.ev_number != AK_TPDU )
229			printf("TPDU 0x%x in REFWAIT!!!!\n", $E.ev_number);
230#		endif TP_DEBUG
231	}
232;
233
234/* applicable in TP4, TP0 */
235SAME			<==			TP_REFWAIT				[ T_DETACH, T_DISC_req ]
236	DEFAULT
237	NULLACTION
238;
239
240/* applicable in TP4, TP0 */
241SAME			<==			TP_CRSENT								 AK_TPDU
242	($P.tp_class == TP_CLASS_0)
243	{
244		/* oh, man is this grotesque or what? */
245		(void) tp_goodack($P, $$.e_cdt, $$.e_seq,  $$.e_subseq);
246		/* but it's necessary because this pseudo-ack may happen
247		 * before the CC arrives, but we HAVE to adjust the
248		 * snduna as a result of the ack, WHENEVER it arrives
249		 */
250	}
251;
252
253/* applicable in TP4, TP0 */
254SAME			<==			TP_CRSENT
255					[ CR_TPDU, DC_TPDU, DT_TPDU, XPD_TPDU,  XAK_TPDU ]
256	DEFAULT
257	NULLACTION
258;
259
260/* applicable in TP4, TP0 */
261SAME			<==			TP_CLOSED					[ DT_TPDU, XPD_TPDU,
262										ER_TPDU, DC_TPDU, AK_TPDU, XAK_TPDU ]
263	DEFAULT
264	NULLACTION
265;
266
267/* TP_CLOSING doesn't exist in TP 0 */
268SAME 			<== 		TP_CLOSING
269					[ CC_TPDU, CR_TPDU, DT_TPDU, XPD_TPDU, AK_TPDU, XAK_TPDU ]
270	DEFAULT
271	NULLACTION
272;
273
274
275/* DC_TPDU doesn't exist in TP 0 */
276SAME			<==			TP_OPEN						  DC_TPDU
277	DEFAULT
278	NULLACTION
279;
280
281/* applicable in TP4, TP0 */
282SAME			<==		 	TP_LISTENING  [DR_TPDU, CC_TPDU, DT_TPDU, XPD_TPDU,
283										 ER_TPDU, DC_TPDU, AK_TPDU, XAK_TPDU ]
284	DEFAULT
285	NULLACTION
286;
287
288/* applicable in TP4, TP0 */
289TP_LISTENING	<==			TP_CLOSED  							T_LISTEN_req
290	DEFAULT
291	NULLACTION
292;
293
294/* applicable in TP4, TP0 */
295TP_CLOSED  		<== 		[ TP_LISTENING, TP_CLOSED ] 			T_DETACH
296	DEFAULT
297	{
298		tp_detach($P);
299	}
300;
301
302TP_CONFIRMING	<==		 TP_LISTENING  								CR_TPDU
303	( $P.tp_class == TP_CLASS_0)
304	{
305		$P.tp_refstate = REF_OPEN; /* has timers ??? */
306	}
307;
308
309TP_CONFIRMING		<==		 TP_LISTENING  							CR_TPDU
310	DEFAULT
311	{
312		IFTRACE(D_CONN)
313			tptrace(TPPTmisc, "CR datalen data", $$.e_datalen, $$.e_data,0,0);
314		ENDTRACE
315		IFDEBUG(D_CONN)
316			printf("CR datalen 0x%x data 0x%x", $$.e_datalen, $$.e_data);
317		ENDDEBUG
318		$P.tp_refstate = REF_OPEN; /* has timers */
319		$P.tp_fcredit = $$.e_cdt;
320
321		if ($$.e_datalen > 0) {
322			/* n/a for class 0 */
323			ASSERT($P.tp_Xrcv.sb_cc == 0);
324			sbappendrecord(&$P.tp_Xrcv, $$.e_data);
325			$$.e_data = MNULL;
326		}
327	}
328;
329
330TP_OPEN		<==		 TP_CONFIRMING  								T_ACPT_req
331	( $P.tp_class == TP_CLASS_0 )
332	{
333		IncStat(ts_tp0_conn);
334		IFTRACE(D_CONN)
335			tptrace(TPPTmisc, "Confiming", $P, 0,0,0);
336		ENDTRACE
337		IFDEBUG(D_CONN)
338			printf("Confirming connection: $P" );
339		ENDDEBUG
340		soisconnected($P.tp_sock);
341		(void) tp_emit(CC_TPDU_type, $P, 0,0, MNULL) ;
342		$P.tp_fcredit = 1;
343	}
344;
345
346TP_AKWAIT		<==		 TP_CONFIRMING  							T_ACPT_req
347	(tp_emit(CC_TPDU_type, $P, 0,0, MCPY($P.tp_ucddata, M_NOWAIT)) == 0)
348	{
349		IncStat(ts_tp4_conn); /* even though not quite open */
350		IFTRACE(D_CONN)
351			tptrace(TPPTmisc, "Confiming", $P, 0,0,0);
352		ENDTRACE
353		IFDEBUG(D_CONN)
354			printf("Confirming connection: $P" );
355		ENDDEBUG
356		tp_getoptions($P);
357		soisconnecting($P.tp_sock);
358		if (($P.tp_rx_strat & TPRX_FASTSTART) && ($P.tp_fcredit > 0))
359			$P.tp_cong_win = $P.tp_fcredit * $P.tp_l_tpdusize;
360		$P.tp_retrans = $P.tp_Nretrans;
361		tp_ctimeout($P, TM_retrans, (int)$P.tp_cc_ticks);
362	}
363;
364
365/* TP4 only */
366TP_CLOSED		<==		 TP_CONFIRMING								T_ACPT_req
367	DEFAULT /* emit failed */
368	{
369		register struct tp_ref *r = $P.tp_refp;
370
371		IFDEBUG(D_CONN)
372			printf("event: CR_TPDU emit CC failed done " );
373		ENDDEBUG
374		soisdisconnected($P.tp_sock);
375		tp_recycle_tsuffix( $P );
376		tp_freeref(r);
377		tp_detach($P);
378	}
379;
380
381/* applicable in TP4, TP0 */
382TP_CRSENT		<==		TP_CLOSED								T_CONN_req
383	DEFAULT
384	{
385		int error;
386		struct mbuf *data = MNULL;
387
388		IFTRACE(D_CONN)
389			tptrace(TPPTmisc, "T_CONN_req flags ucddata", (int)$P.tp_flags,
390			$P.tp_ucddata, 0, 0);
391		ENDTRACE
392		data =  MCPY($P.tp_ucddata, M_WAIT);
393		if (data) {
394			IFDEBUG(D_CONN)
395				printf("T_CONN_req.trans m_copy cc 0x%x\n",
396					$P.tp_ucddata);
397				dump_mbuf(data, "sosnd @ T_CONN_req");
398			ENDDEBUG
399		}
400
401		if (error = tp_emit(CR_TPDU_type, $P, 0, 0, data) )
402			return error; /* driver WON'T change state; will return error */
403
404		$P.tp_refstate = REF_OPEN; /* has timers */
405		if($P.tp_class != TP_CLASS_0) {
406			$P.tp_retrans = $P.tp_Nretrans;
407			tp_ctimeout($P, TM_retrans, (int)$P.tp_cr_ticks);
408		}
409	}
410;
411
412/* applicable in TP4, TP0, but state TP_AKWAIT doesn't apply to TP0 */
413TP_REFWAIT 		<==		[ TP_CRSENT, TP_AKWAIT, TP_OPEN ] 			DR_TPDU
414	DEFAULT
415	{
416		sbflush(&$P.tp_Xrcv); /* purge non-delivered data data */
417		if ($$.e_datalen > 0) {
418			sbappendrecord(&$P.tp_Xrcv, $$.e_data);
419			$$.e_data = MNULL;
420		}
421		if ($P.tp_state == TP_OPEN)
422			tp_indicate(T_DISCONNECT, $P, 0);
423		else {
424			int so_error = ECONNREFUSED;
425			if ($$.e_reason != (E_TP_NO_SESSION ^ TP_ERROR_MASK) &&
426			    $$.e_reason != (E_TP_NO_CR_ON_NC ^ TP_ERROR_MASK) &&
427			    $$.e_reason != (E_TP_REF_OVERFLOW ^ TP_ERROR_MASK))
428				so_error = ECONNABORTED;
429			tp_indicate(T_DISCONNECT, $P, so_error);
430		}
431		tp_soisdisconnected($P);
432		if ($P.tp_class != TP_CLASS_0) {
433			if ($P.tp_state == TP_OPEN ) {
434				tp_euntimeout($P, TM_data_retrans); /* all */
435				tp_cuntimeout($P, TM_retrans);
436				tp_cuntimeout($P, TM_inact);
437				tp_cuntimeout($P, TM_sendack);
438			}
439			tp_cuntimeout($P, TM_retrans);
440			if( $$.e_sref !=  0 )
441				(void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL);
442		}
443	}
444;
445
446SAME 			<==		TP_CLOSED 									DR_TPDU
447	DEFAULT
448	{
449		if( $$.e_sref != 0 )
450			(void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL);
451		/* reference timer already set - reset it to be safe (???) */
452		tp_euntimeout($P, TM_reference); /* all */
453		tp_etimeout($P, TM_reference, (int)$P.tp_refer_ticks);
454	}
455;
456
457/* NBS(34) */
458TP_REFWAIT 		<==  	TP_CRSENT  									ER_TPDU
459	DEFAULT
460	{
461		tp_cuntimeout($P, TM_retrans);
462		tp_indicate(ER_TPDU, $P, $$.e_reason);
463		tp_soisdisconnected($P);
464	}
465;
466
467/* NBS(27) */
468TP_REFWAIT		<==		TP_CLOSING									DR_TPDU
469	DEFAULT
470	{
471		tp_cuntimeout($P, TM_retrans);
472		tp_soisdisconnected($P);
473	}
474;
475/* these two transitions are the same but can't be combined because xebec
476 * can't handle the use of $$.e_reason if they're combined
477 */
478/* NBS(27) */
479TP_REFWAIT		<==		TP_CLOSING									ER_TPDU
480	DEFAULT
481	{
482		tp_indicate(ER_TPDU, $P, $$.e_reason);
483		tp_cuntimeout($P, TM_retrans);
484		tp_soisdisconnected($P);
485	}
486;
487/* NBS(27) */
488TP_REFWAIT		<==		TP_CLOSING									DC_TPDU
489	DEFAULT
490	{
491		tp_cuntimeout($P, TM_retrans);
492		tp_soisdisconnected($P);
493	}
494;
495
496/* NBS(21) */
497SAME 			<== 	TP_CLOSED 						[ CC_TPDU, CR_TPDU ]
498	DEFAULT
499	{	/* don't ask me why we have to do this - spec says so */
500		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_NO_SESSION, MNULL);
501		/* don't bother with retransmissions of the DR */
502	}
503;
504
505/* NBS(34) */
506TP_REFWAIT 		<== 	TP_OPEN  				 					ER_TPDU
507	($P.tp_class == TP_CLASS_0)
508	{
509		tp_soisdisconnecting($P.tp_sock);
510		tp_indicate(ER_TPDU, $P, $$.e_reason);
511		tp_soisdisconnected($P);
512		tp_netcmd( $P, CONN_CLOSE );
513	}
514;
515
516TP_CLOSING 		<== 	[ TP_AKWAIT, TP_OPEN ]  					ER_TPDU
517	DEFAULT
518	{
519		if ($P.tp_state == TP_OPEN) {
520			tp_euntimeout($P, TM_data_retrans); /* all */
521			tp_cuntimeout($P, TM_inact);
522			tp_cuntimeout($P, TM_sendack);
523		}
524		tp_soisdisconnecting($P.tp_sock);
525		tp_indicate(ER_TPDU, $P, $$.e_reason);
526		$P.tp_retrans = $P.tp_Nretrans;
527		tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
528		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_PROTO_ERR, MNULL);
529	}
530;
531/* NBS(6) */
532TP_OPEN			<==		TP_CRSENT									CC_TPDU
533	($P.tp_class == TP_CLASS_0)
534	{
535		tp_cuntimeout($P, TM_retrans);
536		IncStat(ts_tp0_conn);
537		$P.tp_fcredit = 1;
538		soisconnected($P.tp_sock);
539	}
540;
541
542TP_OPEN			<==		TP_CRSENT									CC_TPDU
543	DEFAULT
544	{
545		IFDEBUG(D_CONN)
546			printf("trans: CC_TPDU in CRSENT state flags 0x%x\n",
547				(int)$P.tp_flags);
548		ENDDEBUG
549		IncStat(ts_tp4_conn);
550		$P.tp_fref = $$.e_sref;
551		$P.tp_fcredit = $$.e_cdt;
552		if (($P.tp_rx_strat & TPRX_FASTSTART) && ($$.e_cdt > 0))
553			$P.tp_cong_win = $$.e_cdt * $P.tp_l_tpdusize;
554		tp_getoptions($P);
555		tp_cuntimeout($P, TM_retrans);
556		if ($P.tp_ucddata) {
557			IFDEBUG(D_CONN)
558				printf("dropping user connect data cc 0x%x\n",
559					$P.tp_ucddata->m_len);
560			ENDDEBUG
561			m_freem($P.tp_ucddata);
562			$P.tp_ucddata = 0;
563		}
564		soisconnected($P.tp_sock);
565		if ($$.e_datalen > 0) {
566			ASSERT($P.tp_Xrcv.sb_cc == 0); /* should be empty */
567			sbappendrecord(&$P.tp_Xrcv, $$.e_data);
568			$$.e_data = MNULL;
569		}
570
571		(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
572		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
573	}
574;
575
576/* TP4 only */
577SAME			<==		TP_CRSENT									TM_retrans
578	(	$P.tp_retrans > 0 )
579	{
580		struct mbuf *data = MNULL;
581		int error;
582
583		IncStat(ts_retrans_cr);
584		$P.tp_cong_win = 1 * $P.tp_l_tpdusize;
585		data = MCPY($P.tp_ucddata, M_NOWAIT);
586		if($P.tp_ucddata) {
587			IFDEBUG(D_CONN)
588				printf("TM_retrans.trans m_copy cc 0x%x\n", data);
589				dump_mbuf($P.tp_ucddata, "sosnd @ TM_retrans");
590			ENDDEBUG
591			if( data == MNULL )
592				return ENOBUFS;
593		}
594
595		$P.tp_retrans --;
596		if( error = tp_emit(CR_TPDU_type, $P, 0, 0, data) ) {
597			$P.tp_sock->so_error = error;
598		}
599		tp_ctimeout($P, TM_retrans, (int)$P.tp_cr_ticks);
600	}
601;
602
603/* TP4 only  */
604TP_REFWAIT		<==		TP_CRSENT									TM_retrans
605	DEFAULT /* no more CR retransmissions */
606	{
607		IncStat(ts_conn_gaveup);
608		$P.tp_sock->so_error = ETIMEDOUT;
609		tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
610		tp_soisdisconnected($P);
611	}
612;
613
614/* TP4 only */
615SAME 			<==	 TP_AKWAIT											CR_TPDU
616	DEFAULT
617	/* duplicate CR (which doesn't really exist in the context of
618	 * a connectionless network layer)
619	 * Doesn't occur in class 0.
620	 */
621	{
622		int error;
623		struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
624
625		if( error = tp_emit(CC_TPDU_type, $P, 0, 0, data) ) {
626			$P.tp_sock->so_error = error;
627		}
628		$P.tp_retrans = $P.tp_Nretrans;
629		tp_ctimeout($P, TM_retrans, (int)$P.tp_cc_ticks);
630	}
631;
632
633/* TP4 only */
634TP_OPEN			<==		TP_AKWAIT 										DT_TPDU
635	( IN_RWINDOW( $P, $$.e_seq,
636					$P.tp_rcvnxt, SEQ($P, $P.tp_rcvnxt + $P.tp_lcredit)) )
637	{
638		int doack;
639
640		/*
641		 * Get rid of any confirm or connect data, so that if we
642		 * crash or close, it isn't thought of as disconnect data.
643		 */
644		if ($P.tp_ucddata) {
645			m_freem($P.tp_ucddata);
646			$P.tp_ucddata = 0;
647		}
648		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
649		tp_cuntimeout($P, TM_retrans);
650		soisconnected($P.tp_sock);
651		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
652
653		/* see also next 2 transitions, if you make any changes */
654
655		doack = tp_stash($P, $E);
656		IFDEBUG(D_DATA)
657			printf("tp_stash returns %d\n",doack);
658		ENDDEBUG
659
660		if (doack) {
661			(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL );
662			tp_ctimeout($P, TM_sendack, (int)$P.tp_keepalive_ticks);
663		} else
664			tp_ctimeout( $P, TM_sendack, (int)$P.tp_sendack_ticks);
665
666		IFDEBUG(D_DATA)
667			printf("after stash calling sbwakeup\n");
668		ENDDEBUG
669	}
670;
671
672SAME			<==		TP_OPEN 									DT_TPDU
673	( $P.tp_class == TP_CLASS_0 )
674	{
675		tp0_stash($P, $E);
676		sbwakeup( &$P.tp_sock->so_rcv );
677
678		IFDEBUG(D_DATA)
679			printf("after stash calling sbwakeup\n");
680		ENDDEBUG
681	}
682;
683
684/* TP4 only */
685SAME			<==		TP_OPEN 									DT_TPDU
686	( IN_RWINDOW( $P, $$.e_seq,
687					$P.tp_rcvnxt, SEQ($P, $P.tp_rcvnxt + $P.tp_lcredit)) )
688	{
689		int doack; /* tells if we must ack immediately */
690
691		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
692		sbwakeup( &$P.tp_sock->so_rcv );
693
694		doack = tp_stash($P, $E);
695		IFDEBUG(D_DATA)
696			printf("tp_stash returns %d\n",doack);
697		ENDDEBUG
698
699		if(doack)
700			(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL );
701		else
702			tp_ctimeout_MIN( $P, TM_sendack, (int)$P.tp_sendack_ticks);
703
704		IFDEBUG(D_DATA)
705			printf("after stash calling sbwakeup\n");
706		ENDDEBUG
707	}
708;
709
710/* Not in window  - we must ack under certain circumstances, namely
711 * a) if the seq number is below lwe but > lwe - (max credit ever given)
712 * (to handle lost acks) Can use max-possible-credit for this ^^^.
713 * and
714 * b) seq number is > uwe but < uwe + previously sent & withdrawn credit
715 *
716 * (see 12.2.3.8.1 of ISO spec, p. 73)
717 * We just always ack.
718 */
719/* TP4 only */
720SAME 			<== 	[ TP_OPEN, TP_AKWAIT ]							DT_TPDU
721	DEFAULT /* Not in window */
722	{
723		IFTRACE(D_DATA)
724			tptrace(TPPTmisc, "NIW seq rcvnxt lcredit ",
725				$$.e_seq, $P.tp_rcvnxt, $P.tp_lcredit, 0);
726		ENDTRACE
727		IncStat(ts_dt_niw);
728		m_freem($$.e_data);
729		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
730		(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL );
731	}
732;
733
734/* TP4 only */
735TP_OPEN			<==		TP_AKWAIT										AK_TPDU
736	DEFAULT
737	{
738		if ($P.tp_ucddata) {
739			m_freem($P.tp_ucddata);
740			$P.tp_ucddata = 0;
741		}
742		(void) tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq);
743		tp_cuntimeout($P, TM_retrans);
744
745		soisconnected($P.tp_sock);
746		IFTRACE(D_CONN)
747			struct socket *so = $P.tp_sock;
748			tptrace(TPPTmisc,
749			"called sosiconn: so so_state rcv.sb_sel rcv.sb_flags",
750				so, so->so_state, so->so_rcv.sb_sel, so->so_rcv.sb_flags);
751			tptrace(TPPTmisc,
752			"called sosiconn 2: so_qlen so_error so_rcv.sb_cc so_head",
753				so->so_qlen, so->so_error, so->so_rcv.sb_cc, so->so_head);
754		ENDTRACE
755
756		tp_ctimeout($P, TM_sendack, (int)$P.tp_keepalive_ticks);
757		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
758	}
759;
760
761/* TP4 only */
762TP_OPEN 		<== 	[ TP_OPEN, TP_AKWAIT ]						XPD_TPDU
763	($P.tp_Xrcvnxt == $$.e_seq)
764	{
765		if( $P.tp_state == TP_AKWAIT ) {
766			if ($P.tp_ucddata) {
767				m_freem($P.tp_ucddata);
768				$P.tp_ucddata = 0;
769			}
770			tp_cuntimeout($P, TM_retrans);
771			soisconnected($P.tp_sock);
772			tp_ctimeout($P, TM_sendack, (int)$P.tp_keepalive_ticks);
773			tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
774		}
775		IFTRACE(D_XPD)
776		tptrace(TPPTmisc, "XPD tpdu accepted Xrcvnxt, e_seq datalen m_len\n",
777				$P.tp_Xrcvnxt,$$.e_seq,  $$.e_datalen, $$.e_data->m_len);
778		ENDTRACE
779
780		$P.tp_sock->so_state |= SS_RCVATMARK;
781		$$.e_data->m_flags |= M_EOR;
782		sbinsertoob(&$P.tp_Xrcv, $$.e_data);
783		IFDEBUG(D_XPD)
784			dump_mbuf($$.e_data, "XPD TPDU: tp_Xrcv");
785		ENDDEBUG
786		tp_indicate(T_XDATA, $P, 0);
787		sbwakeup( &$P.tp_Xrcv );
788
789		(void) tp_emit(XAK_TPDU_type, $P, $P.tp_Xrcvnxt, 0, MNULL);
790		SEQ_INC($P, $P.tp_Xrcvnxt);
791	}
792;
793
794/* TP4 only */
795SAME			<==		TP_OPEN 									T_USR_Xrcvd
796	DEFAULT
797	{
798		if( $P.tp_Xrcv.sb_cc == 0 ) {
799			/* kludge for select(): */
800			/* $P.tp_sock->so_state &= ~SS_OOBAVAIL; */
801		}
802	}
803	/* OLD WAY:
804	 * Ack only after the user receives the XPD.  This is better for
805	 * users that use one XPD right after another.
806	 * Acking right away (the NEW WAY, see the prev. transition) is
807	 * better for occasional * XPD, when the receiving user doesn't
808	 * want to read the XPD immediately (which is session's behavior).
809	 *
810		int error = tp_emit(XAK_TPDU_type, $P, $P.tp_Xrcvnxt, 0, MNULL);
811		SEQ_INC($P, $P.tp_Xrcvnxt);
812		return error;
813	*/
814;
815
816/* NOTE: presently if the user doesn't read the connection data
817 * before and expedited data PDU comes in, the connection data will
818 * be dropped. This is a bug.  To avoid it, we need somewhere else
819 * to put the connection data.
820 * On the other hand, we need not to have it sitting around forever.
821 * This is a problem with the idea of trying to accommodate
822 * data on connect w/ a passive-open user interface.
823 */
824/* TP4 only */
825
826SAME	 		<== 	[ TP_AKWAIT, TP_OPEN ] 							XPD_TPDU
827	DEFAULT /* not in window or cdt==0 */
828	{
829		IFTRACE(D_XPD)
830			tptrace(TPPTmisc, "XPD tpdu niw (Xrcvnxt, e_seq) or not cdt (cc)\n",
831				$P.tp_Xrcvnxt, $$.e_seq,  $P.tp_Xrcv.sb_cc , 0);
832		ENDTRACE
833		if( $P.tp_Xrcvnxt != $$.e_seq )
834			IncStat(ts_xpd_niw);
835		if( $P.tp_Xrcv.sb_cc ) {
836			/* might as well kick 'em again */
837			tp_indicate(T_XDATA, $P, 0);
838			IncStat(ts_xpd_dup);
839		}
840		m_freem($$.e_data);
841		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
842		/* don't send an xack because the xak gives "last one received", not
843		 * "next one i expect" (dumb)
844		 */
845	}
846;
847
848/* Occurs (AKWAIT, OPEN) when parent (listening) socket gets aborted, and tries
849 * to detach all its "children"
850 * Also (CRSENT) when user kills a job that's doing a connect()
851 */
852TP_REFWAIT		<== 	TP_CRSENT 										T_DETACH
853	($P.tp_class == TP_CLASS_0)
854	{
855		struct socket *so = $P.tp_sock;
856
857		/* detach from parent socket so it can finish closing */
858		if (so->so_head) {
859			if (!soqremque(so, 0) && !soqremque(so, 1))
860				panic("tp: T_DETACH");
861			so->so_head = 0;
862		}
863		tp_soisdisconnecting($P.tp_sock);
864		tp_netcmd( $P, CONN_CLOSE);
865		tp_soisdisconnected($P);
866	}
867;
868
869/* TP4 only */
870TP_CLOSING		<== [ TP_CLOSING, TP_AKWAIT, TP_CRSENT, TP_CONFIRMING ]	T_DETACH
871	DEFAULT
872	{
873		struct socket *so = $P.tp_sock;
874		struct mbuf *data = MNULL;
875
876		/* detach from parent socket so it can finish closing */
877		if (so->so_head) {
878			if (!soqremque(so, 0) && !soqremque(so, 1))
879				panic("tp: T_DETACH");
880			so->so_head = 0;
881		}
882		if ($P.tp_state != TP_CLOSING) {
883			tp_soisdisconnecting($P.tp_sock);
884			data = MCPY($P.tp_ucddata, M_NOWAIT);
885			(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_NORMAL_DISC, data);
886			$P.tp_retrans = $P.tp_Nretrans;
887			tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
888		}
889	}
890;
891
892TP_REFWAIT		<==		[ TP_OPEN, TP_CRSENT ]		 	  			T_DISC_req
893	( $P.tp_class == TP_CLASS_0 )
894	{
895		tp_soisdisconnecting($P.tp_sock);
896		tp_netcmd( $P, CONN_CLOSE);
897		tp_soisdisconnected($P);
898	}
899;
900
901/* TP4 only */
902TP_CLOSING		<==	[ TP_AKWAIT, TP_OPEN, TP_CRSENT, TP_CONFIRMING ]  T_DISC_req
903	DEFAULT
904	{
905		struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
906
907		if($P.tp_state == TP_OPEN) {
908			tp_euntimeout($P, TM_data_retrans); /* all */
909			tp_cuntimeout($P, TM_inact);
910			tp_cuntimeout($P, TM_sendack);
911		}
912		if (data) {
913			IFDEBUG(D_CONN)
914				printf("T_DISC_req.trans tp_ucddata 0x%x\n",
915					$P.tp_ucddata);
916				dump_mbuf(data, "ucddata @ T_DISC_req");
917			ENDDEBUG
918		}
919		tp_soisdisconnecting($P.tp_sock);
920		$P.tp_retrans = $P.tp_Nretrans;
921		tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
922
923		if( trick_hc )
924			return tp_emit(DR_TPDU_type, $P, 0, $$.e_reason, data);
925	}
926;
927
928/* TP4 only */
929SAME			<==		TP_AKWAIT									TM_retrans
930	( $P.tp_retrans > 0 )
931	{
932		int error;
933		struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
934
935		IncStat(ts_retrans_cc);
936		$P.tp_retrans --;
937		$P.tp_cong_win = 1 * $P.tp_l_tpdusize;
938
939		if( error = tp_emit(CC_TPDU_type, $P, 0, 0, data) )
940			$P.tp_sock->so_error = error;
941		tp_ctimeout($P, TM_retrans, (int)$P.tp_cc_ticks);
942	}
943;
944
945/* TP4 only */
946TP_CLOSING		<==		TP_AKWAIT									TM_retrans
947	DEFAULT  /* out of time */
948	{
949		IncStat(ts_conn_gaveup);
950		tp_soisdisconnecting($P.tp_sock);
951		$P.tp_sock->so_error = ETIMEDOUT;
952		tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
953		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_CONGEST, MNULL);
954		$P.tp_retrans = $P.tp_Nretrans;
955		tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
956	}
957;
958
959/* the retrans timers had better go off BEFORE the inactivity timer does,
960 * if transmissions are going on.
961 * (i.e., TM_inact should be greater than timer for all retrans plus ack
962 * turnaround)
963 */
964/* TP4 only */
965TP_CLOSING 		<==		TP_OPEN		   [ TM_inact, TM_retrans, TM_data_retrans ]
966	DEFAULT
967	{
968		tp_euntimeout($P, TM_data_retrans); /* all */
969		tp_cuntimeout($P, TM_inact);
970		tp_cuntimeout($P, TM_sendack);
971
972		IncStat(ts_conn_gaveup);
973		tp_soisdisconnecting($P.tp_sock);
974		$P.tp_sock->so_error = ETIMEDOUT;
975		tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
976		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_CONGEST_2, MNULL);
977		$P.tp_retrans = $P.tp_Nretrans;
978		tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
979	}
980;
981
982/* TP4 only */
983SAME			<==		TP_OPEN										TM_retrans
984	( $P.tp_retrans > 0 )
985	{
986		$P.tp_cong_win = 1 * $P.tp_l_tpdusize;
987		/* resume XPD */
988		if	( $P.tp_Xsnd.sb_mb )  {
989			struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc);
990			int shift;
991
992			IFTRACE(D_XPD)
993				tptrace(TPPTmisc, "XPD retrans: Xuna Xsndnxt sndnxt snduna",
994					$P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndnxt,
995					$P.tp_snduna);
996			ENDTRACE
997			IFDEBUG(D_XPD)
998				dump_mbuf(m, "XPD retrans emitting M");
999			ENDDEBUG
1000			IncStat(ts_retrans_xpd);
1001			$P.tp_retrans --;
1002			shift = max($P.tp_Nretrans - $P.tp_retrans, 6);
1003			(void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
1004			tp_ctimeout($P, TM_retrans, ((int)$P.tp_dt_ticks) << shift);
1005		}
1006	}
1007;
1008
1009/* TP4 only */
1010SAME 			<==		TP_OPEN									TM_data_retrans
1011	($P.tp_rxtshift < TP_NRETRANS)
1012	{
1013		$P.tp_rxtshift++;
1014		(void) tp_data_retrans($P);
1015	}
1016;
1017
1018/* TP4 only */
1019SAME	 		<==		TP_CLOSING									TM_retrans
1020	(	$P.tp_retrans > 0 )
1021	{
1022		$P.tp_retrans --;
1023		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_DR_NO_REAS, MNULL);
1024		IncStat(ts_retrans_dr);
1025		tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
1026	}
1027;
1028
1029/* TP4 only */
1030TP_REFWAIT 		<==		TP_CLOSING									TM_retrans
1031	DEFAULT	/* no more retrans - gave up */
1032	{
1033		$P.tp_sock->so_error = ETIMEDOUT;
1034		$P.tp_refstate = REF_FROZEN;
1035		tp_recycle_tsuffix( $P );
1036		tp_etimeout($P, TM_reference, (int)$P.tp_refer_ticks);
1037	}
1038;
1039
1040/*
1041 * The resources are kept around until the ref timer goes off.
1042 * The suffices are wiped out sooner so they can be reused right away.
1043 */
1044/* applicable in TP4, TP0 */
1045TP_CLOSED 		<==		TP_REFWAIT 									TM_reference
1046	DEFAULT
1047	{
1048		tp_freeref($P.tp_refp);
1049		tp_detach($P);
1050	}
1051;
1052
1053/* applicable in TP4, TP0 */
1054/* A duplicate CR from connectionless network layer can't happen */
1055SAME 			<== 	TP_OPEN 							[ CR_TPDU, CC_TPDU ]
1056	DEFAULT
1057	{
1058		if( $P.tp_class != TP_CLASS_0) {
1059			tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
1060			if ( $E.ev_number == CC_TPDU )
1061				(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
1062		}
1063		/* ignore it if class 0 - state tables are blank for this */
1064	}
1065;
1066
1067/* applicable in TP4, TP0 */
1068SAME			<== 	TP_OPEN									T_DATA_req
1069	DEFAULT
1070	{
1071		IFTRACE(D_DATA)
1072			tptrace(TPPTmisc, "T_DATA_req sndnxt snduna fcredit, tpcb",
1073				$P.tp_sndnxt, $P.tp_snduna, $P.tp_fcredit, $P);
1074		ENDTRACE
1075
1076		tp_send($P);
1077	}
1078;
1079
1080/* TP4 only */
1081SAME			<==		TP_OPEN										T_XPD_req
1082	DEFAULT
1083		/* T_XPD_req was issued by sosend iff xpd socket buf was empty
1084		 * at time of sosend(),
1085		 * AND (which means) there were no unacknowledged XPD tpdus outstanding!
1086		 */
1087	{
1088		int error = 0;
1089
1090		/* resume XPD */
1091		if	( $P.tp_Xsnd.sb_mb )  {
1092			struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc);
1093			/* m_copy doesn't preserve the m_xlink field, but at this pt.
1094			 * that doesn't matter
1095			 */
1096
1097			IFTRACE(D_XPD)
1098				tptrace(TPPTmisc, "XPD req: Xuna Xsndnxt sndnxt snduna",
1099					$P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndnxt,
1100					$P.tp_snduna);
1101			ENDTRACE
1102			IFDEBUG(D_XPD)
1103				printf("T_XPD_req: sb_cc 0x%x\n", $P.tp_Xsnd.sb_cc);
1104				dump_mbuf(m, "XPD req emitting M");
1105			ENDDEBUG
1106			error =
1107				tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
1108			$P.tp_retrans = $P.tp_Nretrans;
1109
1110			tp_ctimeout($P, TM_retrans, (int)$P.tp_rxtcur);
1111			SEQ_INC($P, $P.tp_Xsndnxt);
1112		}
1113		if(trick_hc)
1114			return error;
1115	}
1116;
1117
1118/* TP4, faked ack in TP0 when cons send completes */
1119SAME 			<==		TP_OPEN 									AK_TPDU
1120	( tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq)  )
1121
1122	/* tp_goodack == true means
1123	 * EITHER it actually acked something heretofore unacknowledged
1124	 * OR no news but the credit should be processed.
1125	 */
1126	{
1127		struct sockbuf *sb = &$P.tp_sock->so_snd;
1128
1129		IFDEBUG(D_ACKRECV)
1130			printf("GOOD ACK seq 0x%x cdt 0x%x\n", $$.e_seq, $$.e_cdt);
1131		ENDDEBUG
1132		if( $P.tp_class != TP_CLASS_0) {
1133			tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
1134		}
1135		sbwakeup(sb);
1136		IFDEBUG(D_ACKRECV)
1137			printf("GOOD ACK new sndnxt 0x%x\n", $P.tp_sndnxt);
1138		ENDDEBUG
1139	}
1140;
1141
1142/* TP4, and TP0 after sending a CC or possibly a CR */
1143SAME			<==		TP_OPEN 			 						 AK_TPDU
1144	DEFAULT
1145	{
1146		IFTRACE(D_ACKRECV)
1147			tptrace(TPPTmisc, "BOGUS ACK fcc_present, tp_r_subseq e_subseq",
1148				$$.e_fcc_present, $P.tp_r_subseq, $$.e_subseq, 0);
1149		ENDTRACE
1150		if( $P.tp_class != TP_CLASS_0 ) {
1151
1152			if ( !$$.e_fcc_present ) {
1153				/* send ACK with FCC */
1154				IncStat( ts_ackreason[_ACK_FCC_] );
1155				(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 1, MNULL);
1156			}
1157			tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
1158		}
1159	}
1160;
1161
1162/* NBS(47) */
1163	/* goes in at *** */
1164		/* just so happens that this is never true now, because we allow
1165		 * only 1 packet in the queue at once (this could be changed)
1166		if	( $P.tp_Xsnd.sb_mb )  {
1167			struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, ??);
1168
1169			(void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
1170			$P.tp_retrans = $P.tp_Nretrans;
1171			tp_ctimeout($P, TM_retrans, (int)$P.tp_xpd_ticks);
1172			SEQ_INC($P, $P.tp_Xsndnxt);
1173		}
1174		 */
1175	/* end of the above hack */
1176
1177/* TP4 only */
1178SAME			<== 	TP_OPEN										XAK_TPDU
1179	( tp_goodXack($P, $$.e_seq) )
1180	/* tp_goodXack checks for good ack, removes the correct
1181	 * tpdu from the queue and  returns 1 if ack was legit, 0 if not.
1182	 * also updates tp_Xuna
1183	 */
1184	{
1185		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
1186		tp_cuntimeout($P, TM_retrans);
1187
1188		sbwakeup( &$P.tp_sock->so_snd );
1189
1190		/* resume normal data */
1191		tp_send($P);
1192	}
1193;
1194
1195/* TP4, and TP0 after sending a CC or possibly a CR */
1196SAME			<==		TP_OPEN 			 						XAK_TPDU
1197	DEFAULT
1198	{
1199		IFTRACE(D_ACKRECV)
1200			tptrace(TPPTmisc, "BOGUS XACK eventtype ", $E.ev_number, 0, 0,0);
1201		ENDTRACE
1202		if( $P.tp_class != TP_CLASS_0 ) {
1203			tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
1204		}
1205	}
1206;
1207
1208/* TP4 only */
1209SAME			<==		TP_OPEN 								TM_sendack
1210	DEFAULT
1211	{
1212		int timo;
1213		IFTRACE(D_TIMER)
1214			tptrace(TPPTsendack, -1, $P.tp_lcredit, $P.tp_sent_uwe,
1215			$P.tp_sent_lcdt, 0);
1216		ENDTRACE
1217		IncPStat($P, tps_n_TMsendack);
1218		(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
1219	}
1220;
1221
1222/* TP0 only */
1223SAME			<==		TP_OPEN 									T_USR_rcvd
1224	($P.tp_class == TP_CLASS_0)
1225	{
1226		if (sbspace(&$P.tp_sock->so_rcv) > 0)
1227			tp0_openflow($P);
1228	}
1229;
1230
1231/* TP4 only */
1232		/* If old credit was zero,
1233		 * we'd better inform other side that we now have space
1234		 * But this is not enough.  Sender might not yet have
1235		 * seen an ack with cdt 0 but it might still think the
1236		 * window is closed, so it's going to wait.
1237		 * Best to send an ack each time.
1238		 * Strictly speaking, this ought to be a function of the
1239		 * general ack strategy.
1240		 */
1241SAME			<==		TP_OPEN 									T_USR_rcvd
1242	DEFAULT
1243	{
1244		if( trick_hc ) {
1245			SeqNum ack_thresh;
1246			/*
1247			 * If the upper window edge has advanced a reasonable
1248			 * amount beyond what was known, send an ACK.
1249			 * A reasonable amount is 2 packets, unless the max window
1250			 * is only 1 or 2 packets, in which case we
1251			 * should send an ack for any advance in the upper window edge.
1252			 */
1253			LOCAL_CREDIT($P);
1254			ack_thresh = SEQ_SUB($P, $P.tp_lcredit + $P.tp_rcvnxt,
1255									 ($P.tp_maxlcredit > 2 ? 2 : 1));
1256			if (SEQ_GT($P, ack_thresh, $P.tp_sent_uwe)) {
1257				IncStat(ts_ackreason[_ACK_USRRCV_]);
1258				$P.tp_flags &= ~TPF_DELACK;
1259				return tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
1260			}
1261		}
1262	}
1263;
1264
1265/* applicable in TP4, TP0 */
1266SAME			<==		TP_REFWAIT 				[ T_USR_rcvd, T_USR_Xrcvd ]
1267	DEFAULT
1268	/* This happens if other end sent a DR when  the user was waiting
1269	 * on a receive.
1270	 * Processing the DR includes putting us in REFWAIT state.
1271	 */
1272	{
1273		if(trick_hc)
1274		return ECONNABORTED;
1275	}
1276;
1277
1278/* TP0 only */
1279TP_REFWAIT		<==		[ TP_OPEN, TP_CRSENT, TP_LISTENING ] 	T_NETRESET
1280	( $P.tp_class != TP_CLASS_4 )
1281		/* 0 or (4 and 0) */
1282		/* in OPEN class will be 0 or 4 but not both */
1283		/* in CRSENT or LISTENING it could be in negotiation, hence both */
1284		/* Actually, this shouldn't ever happen in LISTENING */
1285	{
1286		ASSERT( $P.tp_state != TP_LISTENING );
1287		tp_indicate(T_DISCONNECT, $P, ECONNRESET);
1288		tp_soisdisconnected($P);
1289	}
1290;
1291
1292/* TP4: ignore resets */
1293SAME		<==		[ TP_OPEN, TP_CRSENT, TP_AKWAIT,
1294						TP_CLOSING, TP_LISTENING ] 				T_NETRESET
1295	DEFAULT
1296	NULLACTION
1297;
1298
1299/* applicable in TP4, TP0 */
1300SAME			<==		[ TP_CLOSED, TP_REFWAIT ]				T_NETRESET
1301	DEFAULT
1302	NULLACTION
1303;
1304
1305/* C'EST TOUT */
1306