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