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