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