xref: /csrg-svn/sys/netiso/tp.trans (revision 51214)
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.16 (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.16 (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		IFDEBUG(D_CONN)
370			printf("event: CR_TPDU emit CC failed done " );
371		ENDDEBUG
372		soisdisconnected($P.tp_sock);
373		tp_recycle_tsuffix($P);
374		tp_freeref($P.tp_lref);
375		tp_detach($P);
376	}
377;
378
379/* applicable in TP4, TP0 */
380TP_CRSENT		<==		TP_CLOSED								T_CONN_req
381	DEFAULT
382	{
383		int error;
384		struct mbuf *data = MNULL;
385
386		IFTRACE(D_CONN)
387			tptrace(TPPTmisc, "T_CONN_req flags ucddata", (int)$P.tp_flags,
388			$P.tp_ucddata, 0, 0);
389		ENDTRACE
390		data =  MCPY($P.tp_ucddata, M_WAIT);
391		if (data) {
392			IFDEBUG(D_CONN)
393				printf("T_CONN_req.trans m_copy cc 0x%x\n",
394					$P.tp_ucddata);
395				dump_mbuf(data, "sosnd @ T_CONN_req");
396			ENDDEBUG
397		}
398
399		if (error = tp_emit(CR_TPDU_type, $P, 0, 0, data) )
400			return error; /* driver WON'T change state; will return error */
401
402		$P.tp_refstate = REF_OPEN; /* has timers */
403		if($P.tp_class != TP_CLASS_0) {
404			$P.tp_retrans = $P.tp_Nretrans;
405			tp_ctimeout($P, TM_retrans, (int)$P.tp_cr_ticks);
406		}
407	}
408;
409
410/* applicable in TP4, TP0, but state TP_AKWAIT doesn't apply to TP0 */
411TP_REFWAIT 		<==		[ TP_CRSENT, TP_AKWAIT, TP_OPEN ] 			DR_TPDU
412	DEFAULT
413	{
414		sbflush(&$P.tp_Xrcv); /* purge non-delivered data data */
415		if ($$.e_datalen > 0) {
416			sbappendrecord(&$P.tp_Xrcv, $$.e_data);
417			$$.e_data = MNULL;
418		}
419		if ($P.tp_state == TP_OPEN)
420			tp_indicate(T_DISCONNECT, $P, 0);
421		else {
422			int so_error = ECONNREFUSED;
423			if ($$.e_reason != (E_TP_NO_SESSION ^ TP_ERROR_MASK) &&
424			    $$.e_reason != (E_TP_NO_CR_ON_NC ^ TP_ERROR_MASK) &&
425			    $$.e_reason != (E_TP_REF_OVERFLOW ^ TP_ERROR_MASK))
426				so_error = ECONNABORTED;
427			tp_indicate(T_DISCONNECT, $P, so_error);
428		}
429		tp_soisdisconnected($P);
430		if ($P.tp_class != TP_CLASS_0) {
431			if ($P.tp_state == TP_OPEN ) {
432				tp_euntimeout($P, TM_data_retrans); /* all */
433				tp_cuntimeout($P, TM_retrans);
434				tp_cuntimeout($P, TM_inact);
435				tp_cuntimeout($P, TM_sendack);
436			}
437			tp_cuntimeout($P, TM_retrans);
438			if( $$.e_sref !=  0 )
439				(void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL);
440		}
441	}
442;
443
444SAME 			<==		TP_CLOSED 									DR_TPDU
445	DEFAULT
446	{
447		if( $$.e_sref != 0 )
448			(void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL);
449		/* reference timer already set - reset it to be safe (???) */
450		tp_euntimeout($P, TM_reference); /* all */
451		tp_etimeout($P, TM_reference, (int)$P.tp_refer_ticks);
452	}
453;
454
455/* NBS(34) */
456TP_REFWAIT 		<==  	TP_CRSENT  									ER_TPDU
457	DEFAULT
458	{
459		tp_cuntimeout($P, TM_retrans);
460		tp_indicate(ER_TPDU, $P, $$.e_reason);
461		tp_soisdisconnected($P);
462	}
463;
464
465/* NBS(27) */
466TP_REFWAIT		<==		TP_CLOSING									DR_TPDU
467	DEFAULT
468	{
469		tp_cuntimeout($P, TM_retrans);
470		tp_soisdisconnected($P);
471	}
472;
473/* these two transitions are the same but can't be combined because xebec
474 * can't handle the use of $$.e_reason if they're combined
475 */
476/* NBS(27) */
477TP_REFWAIT		<==		TP_CLOSING									ER_TPDU
478	DEFAULT
479	{
480		tp_indicate(ER_TPDU, $P, $$.e_reason);
481		tp_cuntimeout($P, TM_retrans);
482		tp_soisdisconnected($P);
483	}
484;
485/* NBS(27) */
486TP_REFWAIT		<==		TP_CLOSING									DC_TPDU
487	DEFAULT
488	{
489		tp_cuntimeout($P, TM_retrans);
490		tp_soisdisconnected($P);
491	}
492;
493
494/* NBS(21) */
495SAME 			<== 	TP_CLOSED 						[ CC_TPDU, CR_TPDU ]
496	DEFAULT
497	{	/* don't ask me why we have to do this - spec says so */
498		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_NO_SESSION, MNULL);
499		/* don't bother with retransmissions of the DR */
500	}
501;
502
503/* NBS(34) */
504TP_REFWAIT 		<== 	TP_OPEN  				 					ER_TPDU
505	($P.tp_class == TP_CLASS_0)
506	{
507		tp_soisdisconnecting($P.tp_sock);
508		tp_indicate(ER_TPDU, $P, $$.e_reason);
509		tp_soisdisconnected($P);
510		tp_netcmd( $P, CONN_CLOSE );
511	}
512;
513
514TP_CLOSING 		<== 	[ TP_AKWAIT, TP_OPEN ]  					ER_TPDU
515	DEFAULT
516	{
517		if ($P.tp_state == TP_OPEN) {
518			tp_euntimeout($P, TM_data_retrans); /* all */
519			tp_cuntimeout($P, TM_inact);
520			tp_cuntimeout($P, TM_sendack);
521		}
522		tp_soisdisconnecting($P.tp_sock);
523		tp_indicate(ER_TPDU, $P, $$.e_reason);
524		$P.tp_retrans = $P.tp_Nretrans;
525		tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
526		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_PROTO_ERR, MNULL);
527	}
528;
529/* NBS(6) */
530TP_OPEN			<==		TP_CRSENT									CC_TPDU
531	($P.tp_class == TP_CLASS_0)
532	{
533		tp_cuntimeout($P, TM_retrans);
534		IncStat(ts_tp0_conn);
535		$P.tp_fcredit = 1;
536		soisconnected($P.tp_sock);
537	}
538;
539
540TP_OPEN			<==		TP_CRSENT									CC_TPDU
541	DEFAULT
542	{
543		IFDEBUG(D_CONN)
544			printf("trans: CC_TPDU in CRSENT state flags 0x%x\n",
545				(int)$P.tp_flags);
546		ENDDEBUG
547		IncStat(ts_tp4_conn);
548		$P.tp_fref = $$.e_sref;
549		$P.tp_fcredit = $$.e_cdt;
550		if (($P.tp_rx_strat & TPRX_FASTSTART) && ($$.e_cdt > 0))
551			$P.tp_cong_win = $$.e_cdt * $P.tp_l_tpdusize;
552		tp_getoptions($P);
553		tp_cuntimeout($P, TM_retrans);
554		if ($P.tp_ucddata) {
555			IFDEBUG(D_CONN)
556				printf("dropping user connect data cc 0x%x\n",
557					$P.tp_ucddata->m_len);
558			ENDDEBUG
559			m_freem($P.tp_ucddata);
560			$P.tp_ucddata = 0;
561		}
562		soisconnected($P.tp_sock);
563		if ($$.e_datalen > 0) {
564			ASSERT($P.tp_Xrcv.sb_cc == 0); /* should be empty */
565			sbappendrecord(&$P.tp_Xrcv, $$.e_data);
566			$$.e_data = MNULL;
567		}
568
569		(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
570		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
571	}
572;
573
574/* TP4 only */
575SAME			<==		TP_CRSENT									TM_retrans
576	(	$P.tp_retrans > 0 )
577	{
578		struct mbuf *data = MNULL;
579		int error;
580
581		IncStat(ts_retrans_cr);
582		$P.tp_cong_win = 1 * $P.tp_l_tpdusize;
583		data = MCPY($P.tp_ucddata, M_NOWAIT);
584		if($P.tp_ucddata) {
585			IFDEBUG(D_CONN)
586				printf("TM_retrans.trans m_copy cc 0x%x\n", data);
587				dump_mbuf($P.tp_ucddata, "sosnd @ TM_retrans");
588			ENDDEBUG
589			if( data == MNULL )
590				return ENOBUFS;
591		}
592
593		$P.tp_retrans --;
594		if( error = tp_emit(CR_TPDU_type, $P, 0, 0, data) ) {
595			$P.tp_sock->so_error = error;
596		}
597		tp_ctimeout($P, TM_retrans, (int)$P.tp_cr_ticks);
598	}
599;
600
601/* TP4 only  */
602TP_REFWAIT		<==		TP_CRSENT									TM_retrans
603	DEFAULT /* no more CR retransmissions */
604	{
605		IncStat(ts_conn_gaveup);
606		$P.tp_sock->so_error = ETIMEDOUT;
607		tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
608		tp_soisdisconnected($P);
609	}
610;
611
612/* TP4 only */
613SAME 			<==	 TP_AKWAIT											CR_TPDU
614	DEFAULT
615	/* duplicate CR (which doesn't really exist in the context of
616	 * a connectionless network layer)
617	 * Doesn't occur in class 0.
618	 */
619	{
620		int error;
621		struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
622
623		if( error = tp_emit(CC_TPDU_type, $P, 0, 0, data) ) {
624			$P.tp_sock->so_error = error;
625		}
626		$P.tp_retrans = $P.tp_Nretrans;
627		tp_ctimeout($P, TM_retrans, (int)$P.tp_cc_ticks);
628	}
629;
630
631/* TP4 only */
632TP_OPEN			<==		TP_AKWAIT 										DT_TPDU
633	( IN_RWINDOW( $P, $$.e_seq,
634					$P.tp_rcvnxt, SEQ($P, $P.tp_rcvnxt + $P.tp_lcredit)) )
635	{
636		int doack;
637
638		/*
639		 * Get rid of any confirm or connect data, so that if we
640		 * crash or close, it isn't thought of as disconnect data.
641		 */
642		if ($P.tp_ucddata) {
643			m_freem($P.tp_ucddata);
644			$P.tp_ucddata = 0;
645		}
646		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
647		tp_cuntimeout($P, TM_retrans);
648		soisconnected($P.tp_sock);
649		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
650
651		/* see also next 2 transitions, if you make any changes */
652
653		doack = tp_stash($P, $E);
654		IFDEBUG(D_DATA)
655			printf("tp_stash returns %d\n",doack);
656		ENDDEBUG
657
658		if (doack) {
659			(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL );
660			tp_ctimeout($P, TM_sendack, (int)$P.tp_keepalive_ticks);
661		} else
662			tp_ctimeout( $P, TM_sendack, (int)$P.tp_sendack_ticks);
663
664		IFDEBUG(D_DATA)
665			printf("after stash calling sbwakeup\n");
666		ENDDEBUG
667	}
668;
669
670SAME			<==		TP_OPEN 									DT_TPDU
671	( $P.tp_class == TP_CLASS_0 )
672	{
673		tp0_stash($P, $E);
674		sbwakeup( &$P.tp_sock->so_rcv );
675
676		IFDEBUG(D_DATA)
677			printf("after stash calling sbwakeup\n");
678		ENDDEBUG
679	}
680;
681
682/* TP4 only */
683SAME			<==		TP_OPEN 									DT_TPDU
684	( IN_RWINDOW( $P, $$.e_seq,
685					$P.tp_rcvnxt, SEQ($P, $P.tp_rcvnxt + $P.tp_lcredit)) )
686	{
687		int doack; /* tells if we must ack immediately */
688
689		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
690		sbwakeup( &$P.tp_sock->so_rcv );
691
692		doack = tp_stash($P, $E);
693		IFDEBUG(D_DATA)
694			printf("tp_stash returns %d\n",doack);
695		ENDDEBUG
696
697		if(doack)
698			(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL );
699		else
700			tp_ctimeout_MIN( $P, TM_sendack, (int)$P.tp_sendack_ticks);
701
702		IFDEBUG(D_DATA)
703			printf("after stash calling sbwakeup\n");
704		ENDDEBUG
705	}
706;
707
708/* Not in window  - we must ack under certain circumstances, namely
709 * a) if the seq number is below lwe but > lwe - (max credit ever given)
710 * (to handle lost acks) Can use max-possible-credit for this ^^^.
711 * and
712 * b) seq number is > uwe but < uwe + previously sent & withdrawn credit
713 *
714 * (see 12.2.3.8.1 of ISO spec, p. 73)
715 * We just always ack.
716 */
717/* TP4 only */
718SAME 			<== 	[ TP_OPEN, TP_AKWAIT ]							DT_TPDU
719	DEFAULT /* Not in window */
720	{
721		IFTRACE(D_DATA)
722			tptrace(TPPTmisc, "NIW seq rcvnxt lcredit ",
723				$$.e_seq, $P.tp_rcvnxt, $P.tp_lcredit, 0);
724		ENDTRACE
725		IncStat(ts_dt_niw);
726		m_freem($$.e_data);
727		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
728		(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL );
729	}
730;
731
732/* TP4 only */
733TP_OPEN			<==		TP_AKWAIT										AK_TPDU
734	DEFAULT
735	{
736		if ($P.tp_ucddata) {
737			m_freem($P.tp_ucddata);
738			$P.tp_ucddata = 0;
739		}
740		(void) tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq);
741		tp_cuntimeout($P, TM_retrans);
742
743		soisconnected($P.tp_sock);
744		IFTRACE(D_CONN)
745			struct socket *so = $P.tp_sock;
746			tptrace(TPPTmisc,
747			"called sosiconn: so so_state rcv.sb_sel rcv.sb_flags",
748				so, so->so_state, so->so_rcv.sb_sel, so->so_rcv.sb_flags);
749			tptrace(TPPTmisc,
750			"called sosiconn 2: so_qlen so_error so_rcv.sb_cc so_head",
751				so->so_qlen, so->so_error, so->so_rcv.sb_cc, so->so_head);
752		ENDTRACE
753
754		tp_ctimeout($P, TM_sendack, (int)$P.tp_keepalive_ticks);
755		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
756	}
757;
758
759/* TP4 only */
760TP_OPEN 		<== 	[ TP_OPEN, TP_AKWAIT ]						XPD_TPDU
761	($P.tp_Xrcvnxt == $$.e_seq)
762	{
763		if( $P.tp_state == TP_AKWAIT ) {
764			if ($P.tp_ucddata) {
765				m_freem($P.tp_ucddata);
766				$P.tp_ucddata = 0;
767			}
768			tp_cuntimeout($P, TM_retrans);
769			soisconnected($P.tp_sock);
770			tp_ctimeout($P, TM_sendack, (int)$P.tp_keepalive_ticks);
771			tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
772		}
773		IFTRACE(D_XPD)
774		tptrace(TPPTmisc, "XPD tpdu accepted Xrcvnxt, e_seq datalen m_len\n",
775				$P.tp_Xrcvnxt,$$.e_seq,  $$.e_datalen, $$.e_data->m_len);
776		ENDTRACE
777
778		$P.tp_sock->so_state |= SS_RCVATMARK;
779		$$.e_data->m_flags |= M_EOR;
780		sbinsertoob(&$P.tp_Xrcv, $$.e_data);
781		IFDEBUG(D_XPD)
782			dump_mbuf($$.e_data, "XPD TPDU: tp_Xrcv");
783		ENDDEBUG
784		tp_indicate(T_XDATA, $P, 0);
785		sbwakeup( &$P.tp_Xrcv );
786
787		(void) tp_emit(XAK_TPDU_type, $P, $P.tp_Xrcvnxt, 0, MNULL);
788		SEQ_INC($P, $P.tp_Xrcvnxt);
789	}
790;
791
792/* TP4 only */
793SAME			<==		TP_OPEN 									T_USR_Xrcvd
794	DEFAULT
795	{
796		if( $P.tp_Xrcv.sb_cc == 0 ) {
797			/* kludge for select(): */
798			/* $P.tp_sock->so_state &= ~SS_OOBAVAIL; */
799		}
800	}
801	/* OLD WAY:
802	 * Ack only after the user receives the XPD.  This is better for
803	 * users that use one XPD right after another.
804	 * Acking right away (the NEW WAY, see the prev. transition) is
805	 * better for occasional * XPD, when the receiving user doesn't
806	 * want to read the XPD immediately (which is session's behavior).
807	 *
808		int error = tp_emit(XAK_TPDU_type, $P, $P.tp_Xrcvnxt, 0, MNULL);
809		SEQ_INC($P, $P.tp_Xrcvnxt);
810		return error;
811	*/
812;
813
814/* NOTE: presently if the user doesn't read the connection data
815 * before and expedited data PDU comes in, the connection data will
816 * be dropped. This is a bug.  To avoid it, we need somewhere else
817 * to put the connection data.
818 * On the other hand, we need not to have it sitting around forever.
819 * This is a problem with the idea of trying to accommodate
820 * data on connect w/ a passive-open user interface.
821 */
822/* TP4 only */
823
824SAME	 		<== 	[ TP_AKWAIT, TP_OPEN ] 							XPD_TPDU
825	DEFAULT /* not in window or cdt==0 */
826	{
827		IFTRACE(D_XPD)
828			tptrace(TPPTmisc, "XPD tpdu niw (Xrcvnxt, e_seq) or not cdt (cc)\n",
829				$P.tp_Xrcvnxt, $$.e_seq,  $P.tp_Xrcv.sb_cc , 0);
830		ENDTRACE
831		if( $P.tp_Xrcvnxt != $$.e_seq )
832			IncStat(ts_xpd_niw);
833		if( $P.tp_Xrcv.sb_cc ) {
834			/* might as well kick 'em again */
835			tp_indicate(T_XDATA, $P, 0);
836			IncStat(ts_xpd_dup);
837		}
838		m_freem($$.e_data);
839		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
840		/* don't send an xack because the xak gives "last one received", not
841		 * "next one i expect" (dumb)
842		 */
843	}
844;
845
846/* Occurs (AKWAIT, OPEN) when parent (listening) socket gets aborted, and tries
847 * to detach all its "children"
848 * Also (CRSENT) when user kills a job that's doing a connect()
849 */
850TP_REFWAIT		<== 	TP_CRSENT 										T_DETACH
851	($P.tp_class == TP_CLASS_0)
852	{
853		struct socket *so = $P.tp_sock;
854
855		/* detach from parent socket so it can finish closing */
856		if (so->so_head) {
857			if (!soqremque(so, 0) && !soqremque(so, 1))
858				panic("tp: T_DETACH");
859			so->so_head = 0;
860		}
861		tp_soisdisconnecting($P.tp_sock);
862		tp_netcmd( $P, CONN_CLOSE);
863		tp_soisdisconnected($P);
864	}
865;
866
867/* TP4 only */
868TP_CLOSING		<== [ TP_CLOSING, TP_AKWAIT, TP_CRSENT, TP_CONFIRMING ]	T_DETACH
869	DEFAULT
870	{
871		struct socket *so = $P.tp_sock;
872		struct mbuf *data = MNULL;
873
874		/* detach from parent socket so it can finish closing */
875		if (so->so_head) {
876			if (!soqremque(so, 0) && !soqremque(so, 1))
877				panic("tp: T_DETACH");
878			so->so_head = 0;
879		}
880		if ($P.tp_state != TP_CLOSING) {
881			tp_soisdisconnecting($P.tp_sock);
882			data = MCPY($P.tp_ucddata, M_NOWAIT);
883			(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_NORMAL_DISC, data);
884			$P.tp_retrans = $P.tp_Nretrans;
885			tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
886		}
887	}
888;
889
890TP_REFWAIT		<==		[ TP_OPEN, TP_CRSENT ]		 	  			T_DISC_req
891	( $P.tp_class == TP_CLASS_0 )
892	{
893		tp_soisdisconnecting($P.tp_sock);
894		tp_netcmd( $P, CONN_CLOSE);
895		tp_soisdisconnected($P);
896	}
897;
898
899/* TP4 only */
900TP_CLOSING		<==	[ TP_AKWAIT, TP_OPEN, TP_CRSENT, TP_CONFIRMING ]  T_DISC_req
901	DEFAULT
902	{
903		struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
904
905		if($P.tp_state == TP_OPEN) {
906			tp_euntimeout($P, TM_data_retrans); /* all */
907			tp_cuntimeout($P, TM_inact);
908			tp_cuntimeout($P, TM_sendack);
909		}
910		if (data) {
911			IFDEBUG(D_CONN)
912				printf("T_DISC_req.trans tp_ucddata 0x%x\n",
913					$P.tp_ucddata);
914				dump_mbuf(data, "ucddata @ T_DISC_req");
915			ENDDEBUG
916		}
917		tp_soisdisconnecting($P.tp_sock);
918		$P.tp_retrans = $P.tp_Nretrans;
919		tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
920
921		if( trick_hc )
922			return tp_emit(DR_TPDU_type, $P, 0, $$.e_reason, data);
923	}
924;
925
926/* TP4 only */
927SAME			<==		TP_AKWAIT									TM_retrans
928	( $P.tp_retrans > 0 )
929	{
930		int error;
931		struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
932
933		IncStat(ts_retrans_cc);
934		$P.tp_retrans --;
935		$P.tp_cong_win = 1 * $P.tp_l_tpdusize;
936
937		if( error = tp_emit(CC_TPDU_type, $P, 0, 0, data) )
938			$P.tp_sock->so_error = error;
939		tp_ctimeout($P, TM_retrans, (int)$P.tp_cc_ticks);
940	}
941;
942
943/* TP4 only */
944TP_CLOSING		<==		TP_AKWAIT									TM_retrans
945	DEFAULT  /* out of time */
946	{
947		IncStat(ts_conn_gaveup);
948		tp_soisdisconnecting($P.tp_sock);
949		$P.tp_sock->so_error = ETIMEDOUT;
950		tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
951		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_CONGEST, MNULL);
952		$P.tp_retrans = $P.tp_Nretrans;
953		tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
954	}
955;
956
957/* the retrans timers had better go off BEFORE the inactivity timer does,
958 * if transmissions are going on.
959 * (i.e., TM_inact should be greater than timer for all retrans plus ack
960 * turnaround)
961 */
962/* TP4 only */
963TP_CLOSING 		<==		TP_OPEN		   [ TM_inact, TM_retrans, TM_data_retrans ]
964	DEFAULT
965	{
966		tp_euntimeout($P, TM_data_retrans); /* all */
967		tp_cuntimeout($P, TM_inact);
968		tp_cuntimeout($P, TM_sendack);
969
970		IncStat(ts_conn_gaveup);
971		tp_soisdisconnecting($P.tp_sock);
972		$P.tp_sock->so_error = ETIMEDOUT;
973		tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
974		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_CONGEST_2, MNULL);
975		$P.tp_retrans = $P.tp_Nretrans;
976		tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
977	}
978;
979
980/* TP4 only */
981SAME			<==		TP_OPEN										TM_retrans
982	( $P.tp_retrans > 0 )
983	{
984		$P.tp_cong_win = 1 * $P.tp_l_tpdusize;
985		/* resume XPD */
986		if	( $P.tp_Xsnd.sb_mb )  {
987			struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc);
988			int shift;
989
990			IFTRACE(D_XPD)
991				tptrace(TPPTmisc, "XPD retrans: Xuna Xsndnxt sndnxt snduna",
992					$P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndnxt,
993					$P.tp_snduna);
994			ENDTRACE
995			IFDEBUG(D_XPD)
996				dump_mbuf(m, "XPD retrans emitting M");
997			ENDDEBUG
998			IncStat(ts_retrans_xpd);
999			$P.tp_retrans --;
1000			shift = max($P.tp_Nretrans - $P.tp_retrans, 6);
1001			(void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
1002			tp_ctimeout($P, TM_retrans, ((int)$P.tp_dt_ticks) << shift);
1003		}
1004	}
1005;
1006
1007/* TP4 only */
1008SAME 			<==		TP_OPEN									TM_data_retrans
1009	($P.tp_rxtshift < TP_NRETRANS)
1010	{
1011		$P.tp_rxtshift++;
1012		(void) tp_data_retrans($P);
1013	}
1014;
1015
1016/* TP4 only */
1017SAME	 		<==		TP_CLOSING									TM_retrans
1018	(	$P.tp_retrans > 0 )
1019	{
1020		$P.tp_retrans --;
1021		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_DR_NO_REAS, MNULL);
1022		IncStat(ts_retrans_dr);
1023		tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
1024	}
1025;
1026
1027/* TP4 only */
1028TP_REFWAIT 		<==		TP_CLOSING									TM_retrans
1029	DEFAULT	/* no more retrans - gave up */
1030	{
1031		$P.tp_sock->so_error = ETIMEDOUT;
1032		$P.tp_refstate = REF_FROZEN;
1033		tp_recycle_tsuffix( $P );
1034		tp_etimeout($P, TM_reference, (int)$P.tp_refer_ticks);
1035	}
1036;
1037
1038/*
1039 * The resources are kept around until the ref timer goes off.
1040 * The suffices are wiped out sooner so they can be reused right away.
1041 */
1042/* applicable in TP4, TP0 */
1043TP_CLOSED 		<==		TP_REFWAIT 									TM_reference
1044	DEFAULT
1045	{
1046		tp_freeref($P.tp_lref);
1047		tp_detach($P);
1048	}
1049;
1050
1051/* applicable in TP4, TP0 */
1052/* A duplicate CR from connectionless network layer can't happen */
1053SAME 			<== 	TP_OPEN 							[ CR_TPDU, CC_TPDU ]
1054	DEFAULT
1055	{
1056		if( $P.tp_class != TP_CLASS_0) {
1057			tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
1058			if ( $E.ev_number == CC_TPDU )
1059				(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
1060		}
1061		/* ignore it if class 0 - state tables are blank for this */
1062	}
1063;
1064
1065/* applicable in TP4, TP0 */
1066SAME			<== 	TP_OPEN									T_DATA_req
1067	DEFAULT
1068	{
1069		IFTRACE(D_DATA)
1070			tptrace(TPPTmisc, "T_DATA_req sndnxt snduna fcredit, tpcb",
1071				$P.tp_sndnxt, $P.tp_snduna, $P.tp_fcredit, $P);
1072		ENDTRACE
1073
1074		tp_send($P);
1075	}
1076;
1077
1078/* TP4 only */
1079SAME			<==		TP_OPEN										T_XPD_req
1080	DEFAULT
1081		/* T_XPD_req was issued by sosend iff xpd socket buf was empty
1082		 * at time of sosend(),
1083		 * AND (which means) there were no unacknowledged XPD tpdus outstanding!
1084		 */
1085	{
1086		int error = 0;
1087
1088		/* resume XPD */
1089		if	( $P.tp_Xsnd.sb_mb )  {
1090			struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc);
1091			/* m_copy doesn't preserve the m_xlink field, but at this pt.
1092			 * that doesn't matter
1093			 */
1094
1095			IFTRACE(D_XPD)
1096				tptrace(TPPTmisc, "XPD req: Xuna Xsndnxt sndnxt snduna",
1097					$P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndnxt,
1098					$P.tp_snduna);
1099			ENDTRACE
1100			IFDEBUG(D_XPD)
1101				printf("T_XPD_req: sb_cc 0x%x\n", $P.tp_Xsnd.sb_cc);
1102				dump_mbuf(m, "XPD req emitting M");
1103			ENDDEBUG
1104			error =
1105				tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
1106			$P.tp_retrans = $P.tp_Nretrans;
1107
1108			tp_ctimeout($P, TM_retrans, (int)$P.tp_rxtcur);
1109			SEQ_INC($P, $P.tp_Xsndnxt);
1110		}
1111		if(trick_hc)
1112			return error;
1113	}
1114;
1115
1116/* TP4, faked ack in TP0 when cons send completes */
1117SAME 			<==		TP_OPEN 									AK_TPDU
1118	( tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq)  )
1119
1120	/* tp_goodack == true means
1121	 * EITHER it actually acked something heretofore unacknowledged
1122	 * OR no news but the credit should be processed.
1123	 */
1124	{
1125		struct sockbuf *sb = &$P.tp_sock->so_snd;
1126
1127		IFDEBUG(D_ACKRECV)
1128			printf("GOOD ACK seq 0x%x cdt 0x%x\n", $$.e_seq, $$.e_cdt);
1129		ENDDEBUG
1130		if( $P.tp_class != TP_CLASS_0) {
1131			tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
1132		}
1133		sbwakeup(sb);
1134		IFDEBUG(D_ACKRECV)
1135			printf("GOOD ACK new sndnxt 0x%x\n", $P.tp_sndnxt);
1136		ENDDEBUG
1137	}
1138;
1139
1140/* TP4, and TP0 after sending a CC or possibly a CR */
1141SAME			<==		TP_OPEN 			 						 AK_TPDU
1142	DEFAULT
1143	{
1144		IFTRACE(D_ACKRECV)
1145			tptrace(TPPTmisc, "BOGUS ACK fcc_present, tp_r_subseq e_subseq",
1146				$$.e_fcc_present, $P.tp_r_subseq, $$.e_subseq, 0);
1147		ENDTRACE
1148		if( $P.tp_class != TP_CLASS_0 ) {
1149
1150			if ( !$$.e_fcc_present ) {
1151				/* send ACK with FCC */
1152				IncStat( ts_ackreason[_ACK_FCC_] );
1153				(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 1, MNULL);
1154			}
1155			tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
1156		}
1157	}
1158;
1159
1160/* NBS(47) */
1161	/* goes in at *** */
1162		/* just so happens that this is never true now, because we allow
1163		 * only 1 packet in the queue at once (this could be changed)
1164		if	( $P.tp_Xsnd.sb_mb )  {
1165			struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, ??);
1166
1167			(void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
1168			$P.tp_retrans = $P.tp_Nretrans;
1169			tp_ctimeout($P, TM_retrans, (int)$P.tp_xpd_ticks);
1170			SEQ_INC($P, $P.tp_Xsndnxt);
1171		}
1172		 */
1173	/* end of the above hack */
1174
1175/* TP4 only */
1176SAME			<== 	TP_OPEN										XAK_TPDU
1177	( tp_goodXack($P, $$.e_seq) )
1178	/* tp_goodXack checks for good ack, removes the correct
1179	 * tpdu from the queue and  returns 1 if ack was legit, 0 if not.
1180	 * also updates tp_Xuna
1181	 */
1182	{
1183		tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
1184		tp_cuntimeout($P, TM_retrans);
1185
1186		sbwakeup( &$P.tp_sock->so_snd );
1187
1188		/* resume normal data */
1189		tp_send($P);
1190	}
1191;
1192
1193/* TP4, and TP0 after sending a CC or possibly a CR */
1194SAME			<==		TP_OPEN 			 						XAK_TPDU
1195	DEFAULT
1196	{
1197		IFTRACE(D_ACKRECV)
1198			tptrace(TPPTmisc, "BOGUS XACK eventtype ", $E.ev_number, 0, 0,0);
1199		ENDTRACE
1200		if( $P.tp_class != TP_CLASS_0 ) {
1201			tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
1202		}
1203	}
1204;
1205
1206/* TP4 only */
1207SAME			<==		TP_OPEN 								TM_sendack
1208	DEFAULT
1209	{
1210		int timo;
1211		IFTRACE(D_TIMER)
1212			tptrace(TPPTsendack, -1, $P.tp_lcredit, $P.tp_sent_uwe,
1213			$P.tp_sent_lcdt, 0);
1214		ENDTRACE
1215		IncPStat($P, tps_n_TMsendack);
1216		(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
1217	}
1218;
1219
1220/* TP0 only */
1221SAME			<==		TP_OPEN 									T_USR_rcvd
1222	($P.tp_class == TP_CLASS_0)
1223	{
1224		if (sbspace(&$P.tp_sock->so_rcv) > 0)
1225			tp0_openflow($P);
1226	}
1227;
1228
1229/* TP4 only */
1230		/* If old credit was zero,
1231		 * we'd better inform other side that we now have space
1232		 * But this is not enough.  Sender might not yet have
1233		 * seen an ack with cdt 0 but it might still think the
1234		 * window is closed, so it's going to wait.
1235		 * Best to send an ack each time.
1236		 * Strictly speaking, this ought to be a function of the
1237		 * general ack strategy.
1238		 */
1239SAME			<==		TP_OPEN 									T_USR_rcvd
1240	DEFAULT
1241	{
1242		if( trick_hc ) {
1243			SeqNum ack_thresh;
1244			/*
1245			 * If the upper window edge has advanced a reasonable
1246			 * amount beyond what was known, send an ACK.
1247			 * A reasonable amount is 2 packets, unless the max window
1248			 * is only 1 or 2 packets, in which case we
1249			 * should send an ack for any advance in the upper window edge.
1250			 */
1251			LOCAL_CREDIT($P);
1252			ack_thresh = SEQ_SUB($P, $P.tp_lcredit + $P.tp_rcvnxt,
1253									 ($P.tp_maxlcredit > 2 ? 2 : 1));
1254			if (SEQ_GT($P, ack_thresh, $P.tp_sent_uwe)) {
1255				IncStat(ts_ackreason[_ACK_USRRCV_]);
1256				$P.tp_flags &= ~TPF_DELACK;
1257				return tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
1258			}
1259		}
1260	}
1261;
1262
1263/* applicable in TP4, TP0 */
1264SAME			<==		TP_REFWAIT 				[ T_USR_rcvd, T_USR_Xrcvd ]
1265	DEFAULT
1266	/* This happens if other end sent a DR when  the user was waiting
1267	 * on a receive.
1268	 * Processing the DR includes putting us in REFWAIT state.
1269	 */
1270	{
1271		if(trick_hc)
1272		return ECONNABORTED;
1273	}
1274;
1275
1276/* TP0 only */
1277TP_REFWAIT		<==		[ TP_OPEN, TP_CRSENT, TP_LISTENING ] 	T_NETRESET
1278	( $P.tp_class != TP_CLASS_4 )
1279		/* 0 or (4 and 0) */
1280		/* in OPEN class will be 0 or 4 but not both */
1281		/* in CRSENT or LISTENING it could be in negotiation, hence both */
1282		/* Actually, this shouldn't ever happen in LISTENING */
1283	{
1284		ASSERT( $P.tp_state != TP_LISTENING );
1285		tp_indicate(T_DISCONNECT, $P, ECONNRESET);
1286		tp_soisdisconnected($P);
1287	}
1288;
1289
1290/* TP4: ignore resets */
1291SAME		<==		[ TP_OPEN, TP_CRSENT, TP_AKWAIT,
1292						TP_CLOSING, TP_LISTENING ] 				T_NETRESET
1293	DEFAULT
1294	NULLACTION
1295;
1296
1297/* applicable in TP4, TP0 */
1298SAME			<==		[ TP_CLOSED, TP_REFWAIT ]				T_NETRESET
1299	DEFAULT
1300	NULLACTION
1301;
1302
1303/* C'EST TOUT */
1304