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