xref: /csrg-svn/sys/netiso/tp.trans (revision 42944)
1/***********************************************************
2		Copyright IBM Corporation 1987
3
4                      All Rights Reserved
5
6Permission to use, copy, modify, and distribute this software and its
7documentation for any purpose and without fee is hereby granted,
8provided that the above copyright notice appear in all copies and that
9both that copyright notice and this permission notice appear in
10supporting documentation, and that the name of IBM not be
11used in advertising or publicity pertaining to distribution of the
12software without specific, written prior permission.
13
14IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
15ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
16IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
17ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
18WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
19ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20SOFTWARE.
21
22******************************************************************/
23
24/*
25 * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
26 */
27/* $Header: tp.trans,v 5.1 88/10/12 12:22:07 root Exp $
28 *
29 * Transition file for TP.
30 *
31 * DO NOT:
32 * - change the order of any of the events or states.  to do so will
33 *   make tppt, netstat, etc. cease working.
34 *
35 * NOTE:
36 * some hooks exist for data on (dis)connect, but it's ***NOT***SUPPORTED***
37 * (read: may not work!)
38 *
39 * I tried to put everything that causes a change of state in here, hence
40 * there are some seemingly trivial events  like T_DETACH and T_LISTEN_req.
41 *
42 * Almost everything having to do w/ setting & cancelling timers is here
43 * but once it was debugged, I moved the setting of the
44 * keepalive (sendack) timer to tp_emit(), where an AK_TPDU is sent.
45 * This is so the code wouldn't be duplicated all over creation in here.
46 *
47 */
48*PROTOCOL tp
49
50*INCLUDE
51
52{
53/*	@(#)tp.trans	7.5 (Berkeley) 06/06/90 */
54#include "param.h"
55#include "socket.h"
56#include "socketvar.h"
57#include "protosw.h"
58#include "mbuf.h"
59#include "time.h"
60#include "errno.h"
61#include "../netiso/tp_param.h"
62#include "../netiso/tp_stat.h"
63#include "../netiso/tp_pcb.h"
64#include "../netiso/tp_tpdu.h"
65#include "../netiso/argo_debug.h"
66#include "../netiso/tp_trace.h"
67#include "../netiso/iso_errno.h"
68#include "../netiso/tp_seq.h"
69#include "../netiso/cons.h"
70
71#define DRIVERTRACE TPPTdriver
72#define sbwakeup(sb)	sowakeup(p->tp_sock, sb);
73#define MCPY(d, w) (d ? m_copym(d, 0, (int)M_COPYALL, w): 0)
74
75static 	trick_hc = 1;
76
77int 	tp_emit(),
78		tp_goodack(),				tp_goodXack(),
79		tp_stash()
80;
81void	tp_indicate(),				tp_getoptions(),
82		tp_soisdisconnecting(), 	tp_soisdisconnected(),
83		tp_recycle_tsuffix(),
84		tp_etimeout(),				tp_euntimeout(),
85		tp_euntimeout_lss(),		tp_ctimeout(),
86		tp_cuntimeout(),			tp_ctimeout_MIN(),
87		tp_freeref(),				tp_detach(),
88		tp0_stash(), 				tp0_send(),
89		tp_netcmd(),				tp_send()
90;
91
92typedef  struct tp_pcb tpcb_struct;
93
94
95}
96
97*PCB    tpcb_struct 	SYNONYM  P
98
99*STATES
100
101TP_CLOSED
102TP_CRSENT
103TP_AKWAIT
104TP_OPEN
105TP_CLOSING
106TP_REFWAIT
107TP_LISTENING	/* Local to this implementation */
108TP_CONFIRMING	/* Local to this implementation */
109
110*EVENTS		{ struct timeval e_time; } 		SYNONYM  E
111
112 /*
113  * C (typically cancelled) timers  -
114  *
115  * let these be the first ones so for the sake of convenience
116  * their values are 0--> n-1
117  * DO NOT CHANGE THE ORDER OF THESE TIMER EVENTS!!
118  */
119 TM_inact
120 TM_retrans
121				/* TM_retrans is used for all
122				 * simple retransmissions - CR,CC,XPD,DR
123				 */
124
125 TM_sendack
126				/* TM_sendack does dual duty - keepalive AND sendack.
127				 * It's set w/ keepalive-ticks every time an ack is sent.
128				 * (this is done in (void) tp_emit() ).
129				 * It's cancelled and reset whenever a DT
130				 * arrives and it doesn't require immediate acking.
131				 * Note that in this case it's set w/ the minimum of
132				 * its prev value and the sendack-ticks value so the
133				 * purpose of the keepalive is preserved.
134				 */
135 TM_notused
136
137 /*
138  * E (typically expired) timers - these may be in any order.
139  * These cause procedures to be executed directly; may not
140  * cause an 'event' as we know them here.
141  */
142 TM_reference		{ SeqNum e_low; SeqNum e_high; int e_retrans; }
143 TM_data_retrans	{ SeqNum e_low; SeqNum e_high; int e_retrans; }
144
145/* NOTE: in tp_input is a minor optimization that assumes that
146 * for all tpdu types that can take e_data and e_datalen, these
147 * fields fall in the same place in the event structure, that is,
148 * e_data is the first field and e_datalen is the 2nd field.
149 */
150
151 ER_TPDU  	 	{
152				  u_char		e_reason;
153				}
154 CR_TPDU  	 	{ struct mbuf 	*e_data;	/* first field */
155				  int 			e_datalen; /* 2nd field */
156				  u_int			e_cdt;
157				}
158 DR_TPDU   	 	{ struct mbuf 	*e_data;	/* first field */
159				  int 			e_datalen; /* 2nd field */
160				  u_short		e_sref;
161				  u_char		e_reason;
162				}
163 DC_TPDU
164 CC_TPDU   	 	{ struct mbuf 	*e_data;	/* first field */
165				  int 			e_datalen; /* 2nd field */
166				  u_short		e_sref;
167				  u_int			e_cdt;
168				}
169 AK_TPDU		{ u_int			e_cdt;
170				  SeqNum 	 	e_seq;
171				  SeqNum 	 	e_subseq;
172				  u_char 	 	e_fcc_present;
173				}
174 DT_TPDU		{ struct mbuf	*e_data; 	/* first field */
175				  int 			e_datalen; /* 2nd field */
176				  u_int 		e_eot;
177				  SeqNum		e_seq;
178				}
179 XPD_TPDU		{ struct mbuf 	*e_data;	/* first field */
180				  int 			e_datalen; 	/* 2nd field */
181				  SeqNum 		e_seq;
182				}
183 XAK_TPDU		{ SeqNum 		e_seq;		}
184
185 T_CONN_req
186 T_DISC_req		{ u_char		e_reason; 	}
187 T_LISTEN_req
188 T_DATA_req
189 T_XPD_req
190 T_USR_rcvd
191 T_USR_Xrcvd
192 T_DETACH
193 T_NETRESET
194 T_ACPT_req
195
196
197*TRANSITIONS
198
199
200/* TP_AKWAIT doesn't exist in TP 0 */
201SAME			<==			TP_AKWAIT			[ CC_TPDU, DC_TPDU, XAK_TPDU ]
202	DEFAULT
203	NULLACTION
204;
205
206
207/* applicable in TP4, TP0 */
208SAME			<==			TP_REFWAIT								DR_TPDU
209	( $$.e_sref !=  0 )
210	{
211		(void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL);
212	}
213;
214
215/* applicable in TP4, TP0 */
216SAME			<==			TP_REFWAIT			[ CR_TPDU, CC_TPDU, DT_TPDU,
217					DR_TPDU, XPD_TPDU, AK_TPDU, XAK_TPDU, DC_TPDU, ER_TPDU ]
218	DEFAULT
219	{
220#		ifdef TP_DEBUG
221		if( $E.ev_number != AK_TPDU )
222			printf("TPDU 0x%x in REFWAIT!!!!\n", $E.ev_number);
223#		endif TP_DEBUG
224	}
225;
226
227/* applicable in TP4, TP0 */
228SAME			<==			TP_REFWAIT				[ T_DETACH, T_DISC_req ]
229	DEFAULT
230	NULLACTION
231;
232
233/* applicable in TP4, TP0 */
234SAME			<==			TP_CRSENT								 AK_TPDU
235	($P.tp_class == TP_CLASS_0)
236	{
237		/* oh, man is this grotesque or what? */
238		(void) tp_goodack($P, $$.e_cdt, $$.e_seq,  $$.e_subseq);
239		/* but it's necessary because this pseudo-ack may happen
240		 * before the CC arrives, but we HAVE to adjust the
241		 * snduna as a result of the ack, WHENEVER it arrives
242		 */
243	}
244;
245
246/* applicable in TP4, TP0 */
247SAME			<==			TP_CRSENT
248					[ CR_TPDU, DC_TPDU, DT_TPDU, XPD_TPDU,  XAK_TPDU ]
249	DEFAULT
250	NULLACTION
251;
252
253/* applicable in TP4, TP0 */
254SAME			<==			TP_CLOSED					[ DT_TPDU, XPD_TPDU,
255										ER_TPDU, DC_TPDU, AK_TPDU, XAK_TPDU ]
256	DEFAULT
257	NULLACTION
258;
259
260/* TP_CLOSING doesn't exist in TP 0 */
261SAME 			<== 		TP_CLOSING
262					[ CC_TPDU, CR_TPDU, DT_TPDU, XPD_TPDU, AK_TPDU, XAK_TPDU ]
263	DEFAULT
264	NULLACTION
265;
266
267
268/* DC_TPDU doesn't exist in TP 0 */
269SAME			<==			TP_OPEN						  DC_TPDU
270	DEFAULT
271	NULLACTION
272;
273
274/* applicable in TP4, TP0 */
275SAME			<==		 	TP_LISTENING  [DR_TPDU, CC_TPDU, DT_TPDU, XPD_TPDU,
276										 ER_TPDU, DC_TPDU, AK_TPDU, XAK_TPDU ]
277	DEFAULT
278	NULLACTION
279;
280
281/* applicable in TP4, TP0 */
282TP_LISTENING	<==			TP_CLOSED  							T_LISTEN_req
283	DEFAULT
284	NULLACTION
285;
286
287/* applicable in TP4, TP0 */
288TP_CLOSED  		<== 		[ TP_LISTENING, TP_CLOSED ] 			T_DETACH
289	DEFAULT
290	{
291		tp_detach($P);
292	}
293;
294
295TP_CONFIRMING	<==		 TP_LISTENING  								CR_TPDU
296	( $P.tp_class == TP_CLASS_0)
297	{
298		$P.tp_refp->tpr_state = REF_OPEN; /* has timers ??? */
299	}
300;
301
302TP_CONFIRMING		<==		 TP_LISTENING  							CR_TPDU
303	DEFAULT
304	{
305		IFTRACE(D_CONN)
306			tptrace(TPPTmisc, "CR datalen data", $$.e_datalen, $$.e_data,0,0);
307		ENDTRACE
308		IFDEBUG(D_CONN)
309			printf("CR datalen 0x%x data 0x%x", $$.e_datalen, $$.e_data);
310		ENDDEBUG
311		$P.tp_refp->tpr_state = REF_OPEN; /* has timers */
312		$P.tp_fcredit = $$.e_cdt;
313
314		if ($$.e_datalen > 0) {
315			/* n/a for class 0 */
316			ASSERT($P.tp_Xrcv.sb_cc == 0);
317			sbappendrecord(&$P.tp_Xrcv, $$.e_data);
318			/*$P.tp_flags |= TPF_CONN_DATA_IN;*/
319			$$.e_data = MNULL;
320		}
321	}
322;
323
324TP_OPEN		<==		 TP_CONFIRMING  								T_ACPT_req
325	( $P.tp_class == TP_CLASS_0 )
326	{
327		IncStat(ts_tp0_conn);
328		IFTRACE(D_CONN)
329			tptrace(TPPTmisc, "Confiming", $P, 0,0,0);
330		ENDTRACE
331		IFDEBUG(D_CONN)
332			printf("Confirming connection: $P" );
333		ENDDEBUG
334		soisconnected($P.tp_sock);
335		(void) tp_emit(CC_TPDU_type, $P, 0,0, MNULL) ;
336		$P.tp_fcredit = 1;
337	}
338;
339
340TP_AKWAIT		<==		 TP_CONFIRMING  							T_ACPT_req
341	(tp_emit(CC_TPDU_type, $P, 0,0, MCPY($P.tp_ucddata, M_NOWAIT)) == 0)
342	{
343		IncStat(ts_tp4_conn); /* even though not quite open */
344		IFTRACE(D_CONN)
345			tptrace(TPPTmisc, "Confiming", $P, 0,0,0);
346		ENDTRACE
347		IFDEBUG(D_CONN)
348			printf("Confirming connection: $P" );
349		ENDDEBUG
350		soisconnecting($P.tp_sock);
351		if($P.tp_rx_strat & TPRX_FASTSTART)
352			$P.tp_cong_win = $P.tp_fcredit;
353		$P.tp_retrans = $P.tp_Nretrans;
354		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_cc_ticks);
355	}
356;
357
358/* TP4 only */
359TP_CLOSED		<==		 TP_CONFIRMING								T_ACPT_req
360	DEFAULT /* emit failed */
361	{
362		register struct tp_ref *r = $P.tp_refp;
363
364		IFDEBUG(D_CONN)
365			printf("event: CR_TPDU emit CC failed done " );
366		ENDDEBUG
367		soisdisconnected($P.tp_sock);
368		tp_recycle_tsuffix( $P );
369		tp_freeref(r);
370		tp_detach($P);
371	}
372;
373
374/* applicable in TP4, TP0 */
375TP_CRSENT		<==		TP_CLOSED								T_CONN_req
376	DEFAULT
377	{
378		int error;
379		struct mbuf *data = MNULL;
380
381		IFTRACE(D_CONN)
382			tptrace(TPPTmisc, "T_CONN_req flags ucddata", (int)$P.tp_flags,
383			$P.tp_ucddata, 0, 0);
384		ENDTRACE
385		data =  MCPY($P.tp_ucddata, M_WAIT);
386		if (data) {
387			IFDEBUG(D_CONN)
388				printf("T_CONN_req.trans m_copy cc 0x%x\n",
389					$P.tp_ucddata);
390				dump_mbuf(data, "sosnd @ T_CONN_req");
391			ENDDEBUG
392		}
393
394		if (error = tp_emit(CR_TPDU_type, $P, 0, 0, data) )
395			return error; /* driver WON'T change state; will return error */
396
397		$P.tp_refp->tpr_state = REF_OPEN; /* has timers */
398		if($P.tp_class != TP_CLASS_0) {
399			$P.tp_retrans = $P.tp_Nretrans;
400			tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_cr_ticks);
401		}
402	}
403;
404
405/* applicable in TP4, TP0, but state TP_AKWAIT doesn't apply to TP0 */
406TP_REFWAIT 		<==		[ TP_CRSENT, TP_AKWAIT, TP_OPEN ] 			DR_TPDU
407	DEFAULT
408	{
409		if ($$.e_datalen > 0 && $P.tp_class != TP_CLASS_0) {
410			/*sbdrop(&$P.tp_Xrcv, $P.tp_Xrcv.sb_cc); /* purge expedited data */
411			sbflush(&$P.tp_Xrcv);
412			$P.tp_flags |= TPF_DISC_DATA_IN;
413			sbappendrecord(&$P.tp_Xrcv, $$.e_data);
414			$$.e_data = MNULL;
415		}
416		tp_indicate(T_DISCONNECT, $P, TP_ERROR_MASK | (u_short)$$.e_reason);
417		tp_soisdisconnected($P);
418		if ($P.tp_class != TP_CLASS_0) {
419			if ($P.tp_state == TP_OPEN ) {
420				tp_euntimeout($P.tp_refp, TM_data_retrans); /* all */
421				tp_cuntimeout($P.tp_refp, TM_retrans);
422				tp_cuntimeout($P.tp_refp, TM_inact);
423				tp_cuntimeout($P.tp_refp, TM_sendack);
424			}
425			tp_cuntimeout($P.tp_refp, TM_retrans);
426			if( $$.e_sref !=  0 )
427				(void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL);
428		}
429	}
430;
431
432SAME 			<==		TP_CLOSED 									DR_TPDU
433	DEFAULT
434	{
435		if( $$.e_sref != 0 )
436			(void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL);
437		/* reference timer already set - reset it to be safe (???) */
438		tp_euntimeout($P.tp_refp, TM_reference); /* all */
439		tp_etimeout($P.tp_refp, TM_reference, 0, 0, 0, (int)$P.tp_refer_ticks);
440	}
441;
442
443/* NBS(34) */
444TP_REFWAIT 		<==  	TP_CRSENT  									ER_TPDU
445	DEFAULT
446	{
447		tp_cuntimeout($P.tp_refp, TM_retrans);
448		tp_indicate(T_DISCONNECT, $P,
449			TP_ERROR_MASK |(u_short)($$.e_reason | 0x40));
450		tp_soisdisconnected($P);
451	}
452;
453
454/* NBS(27) */
455TP_REFWAIT		<==		TP_CLOSING									DR_TPDU
456	DEFAULT
457	{
458		$P.tp_sock->so_error = (u_short)$$.e_reason;
459		tp_cuntimeout($P.tp_refp, TM_retrans);
460		tp_soisdisconnected($P);
461	}
462;
463/* these two transitions are the same but can't be combined because xebec
464 * can't handle the use of $$.e_reason if they're combined
465 */
466/* NBS(27) */
467TP_REFWAIT		<==		TP_CLOSING									ER_TPDU
468	DEFAULT
469	{
470		$P.tp_sock->so_error = (u_short)$$.e_reason;
471		tp_cuntimeout($P.tp_refp, TM_retrans);
472		tp_soisdisconnected($P);
473	}
474;
475/* NBS(27) */
476TP_REFWAIT		<==		TP_CLOSING									DC_TPDU
477	DEFAULT
478	{
479		tp_cuntimeout($P.tp_refp, TM_retrans);
480		tp_soisdisconnected($P);
481	}
482;
483
484/* NBS(21) */
485SAME 			<== 	TP_CLOSED 						[ CC_TPDU, CR_TPDU ]
486	DEFAULT
487	{	/* don't ask me why we have to do this - spec says so */
488		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_NO_SESSION, MNULL);
489		/* don't bother with retransmissions of the DR */
490	}
491;
492
493/* NBS(34) */
494TP_REFWAIT 		<== 	TP_OPEN  				 					ER_TPDU
495	($P.tp_class == TP_CLASS_0)
496	{
497		tp_soisdisconnecting($P.tp_sock);
498		tp_indicate(T_DISCONNECT, $P,
499			TP_ERROR_MASK |(u_short)($$.e_reason | 0x40));
500
501		tp_soisdisconnected($P);
502		tp_netcmd( $P, CONN_CLOSE );
503	}
504;
505
506TP_CLOSING 		<== 	[ TP_AKWAIT, TP_OPEN ]  					ER_TPDU
507	DEFAULT
508	{
509		if ($P.tp_state == TP_OPEN) {
510			tp_euntimeout($P.tp_refp, TM_data_retrans); /* all */
511			tp_cuntimeout($P.tp_refp, TM_inact);
512			tp_cuntimeout($P.tp_refp, TM_sendack);
513		}
514		tp_soisdisconnecting($P.tp_sock);
515		tp_indicate(T_DISCONNECT, $P,
516			TP_ERROR_MASK |(u_short)($$.e_reason | 0x40));
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)
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			$P.tp_flags |= TPF_CONN_DATA_IN;
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  /* && $P.tp_Xrcv.sb_cc == 0*/)
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		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			/*$P.tp_flags &= ~TPF_XPD_PRESENT;*/
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#ifdef notdef
834			if( $P.tp_flags & TPF_CONN_DATA_IN ) {
835				/* user isn't reading the connection data; see note above */
836				sbdrop(&$P.tp_Xrcv, $P.tp_Xrcv.sb_cc);
837				$P.tp_flags &= ~TPF_CONN_DATA_IN;
838			}
839#endif notdef
840			/* might as well kick 'em again */
841			tp_indicate(T_XDATA, $P, 0);
842			IncStat(ts_xpd_dup);
843		}
844		m_freem($$.e_data);
845		tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
846		/* don't send an xack because the xak gives "last one received", not
847		 * "next one i expect" (dumb)
848		 */
849	}
850;
851
852/* Occurs (AKWAIT, OPEN) when parent (listening) socket gets aborted, and tries
853 * to detach all its "children"
854 * Also (CRSENT) when user kills a job that's doing a connect()
855 */
856TP_REFWAIT		<== 	TP_CRSENT 										T_DETACH
857	($P.tp_class == TP_CLASS_0)
858	{
859		struct socket *so = $P.tp_sock;
860
861		/* detach from parent socket so it can finish closing */
862		if (so->so_head) {
863			if (!soqremque(so, 0) && !soqremque(so, 1))
864				panic("tp: T_DETACH");
865			so->so_head = 0;
866		}
867		tp_soisdisconnecting($P.tp_sock);
868		tp_netcmd( $P, CONN_CLOSE);
869		tp_soisdisconnected($P);
870	}
871;
872
873/* TP4 only */
874TP_CLOSING		<== [ TP_CLOSING, TP_AKWAIT, TP_CRSENT, TP_CONFIRMING ]	T_DETACH
875	DEFAULT
876	{
877		struct socket *so = $P.tp_sock;
878		struct mbuf *data = MNULL;
879
880		/* detach from parent socket so it can finish closing */
881		if (so->so_head) {
882			if (!soqremque(so, 0) && !soqremque(so, 1))
883				panic("tp: T_DETACH");
884			so->so_head = 0;
885		}
886		if ($P.tp_state != TP_CLOSING) {
887			tp_soisdisconnecting($P.tp_sock);
888			data = MCPY($P.tp_ucddata, M_NOWAIT);
889			(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_NORMAL_DISC, data);
890			$P.tp_retrans = $P.tp_Nretrans;
891			tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks);
892		}
893	}
894;
895
896TP_REFWAIT		<==		[ TP_OPEN, TP_CRSENT ]		 	  			T_DISC_req
897	( $P.tp_class == TP_CLASS_0 )
898	{
899		tp_soisdisconnecting($P.tp_sock);
900		tp_netcmd( $P, CONN_CLOSE);
901		tp_soisdisconnected($P);
902	}
903;
904
905/* TP4 only */
906TP_CLOSING		<==	[ TP_AKWAIT, TP_OPEN, TP_CRSENT, TP_CONFIRMING ]  T_DISC_req
907	DEFAULT
908	{
909		struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
910
911		if($P.tp_state == TP_OPEN) {
912			tp_euntimeout($P.tp_refp, TM_data_retrans); /* all */
913			tp_cuntimeout($P.tp_refp, TM_inact);
914			tp_cuntimeout($P.tp_refp, TM_sendack);
915		}
916		if (data) {
917			IFDEBUG(D_CONN)
918				printf("T_DISC_req.trans tp_ucddata 0x%x\n",
919					$P.tp_ucddata);
920				dump_mbuf(data, "ucddata @ T_DISC_req");
921			ENDDEBUG
922		}
923		tp_soisdisconnecting($P.tp_sock);
924		$P.tp_retrans = $P.tp_Nretrans;
925		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks);
926
927		if( trick_hc )
928			return tp_emit(DR_TPDU_type, $P, 0, $$.e_reason, data);
929	}
930;
931
932/* TP4 only */
933SAME			<==		TP_AKWAIT									TM_retrans
934	( $P.tp_retrans > 0 )
935	{
936		int error;
937		struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
938
939		IncStat(ts_retrans_cc);
940		$P.tp_retrans --;
941		$P.tp_cong_win = 1;
942		$P.tp_ackrcvd = 0;
943
944		if( error = tp_emit(CC_TPDU_type, $P, 0, 0, data) )
945			$P.tp_sock->so_error = error;
946		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_cc_ticks);
947	}
948;
949
950/* TP4 only */
951TP_CLOSING		<==		TP_AKWAIT									TM_retrans
952	DEFAULT  /* out of time */
953	{
954		IncStat(ts_conn_gaveup);
955		tp_soisdisconnecting($P.tp_sock);
956		$P.tp_sock->so_error = ETIMEDOUT;
957		tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
958		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_CONGEST, MNULL);
959		$P.tp_retrans = $P.tp_Nretrans;
960		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks);
961	}
962;
963
964/* the retrans timers had better go off BEFORE the inactivity timer does,
965 * if transmissions are going on.
966 * (i.e., TM_inact should be greater than timer for all retrans plus ack
967 * turnaround)
968 */
969/* TP4 only */
970TP_CLOSING 		<==		TP_OPEN		   [ TM_inact, TM_retrans, TM_data_retrans ]
971	DEFAULT
972	{
973		tp_euntimeout($P.tp_refp, TM_data_retrans); /* all */
974		tp_cuntimeout($P.tp_refp, TM_inact);
975		tp_cuntimeout($P.tp_refp, TM_sendack);
976
977		IncStat(ts_conn_gaveup);
978		tp_soisdisconnecting($P.tp_sock);
979		$P.tp_sock->so_error = ETIMEDOUT;
980		tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
981		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_CONGEST_2, MNULL);
982		$P.tp_retrans = $P.tp_Nretrans;
983		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks);
984	}
985;
986
987/* TP4 only */
988SAME			<==		TP_OPEN										TM_retrans
989	( $P.tp_retrans > 0 )
990	{
991		$P.tp_cong_win = 1;
992		$P.tp_ackrcvd = 0;
993		/* resume XPD */
994		if	( $P.tp_Xsnd.sb_mb )  {
995			struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc);
996			/* m_copy doesn't preserve the m_xlink field, but at this pt.
997			 * that doesn't matter
998			 */
999
1000			IFTRACE(D_XPD)
1001				tptrace(TPPTmisc, "XPD retrans: Xuna Xsndnxt sndhiwat snduna",
1002					$P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndhiwat,
1003					$P.tp_snduna);
1004			ENDTRACE
1005			IFDEBUG(D_XPD)
1006				dump_mbuf(m, "XPD retrans emitting M");
1007			ENDDEBUG
1008			IncStat(ts_retrans_xpd);
1009			$P.tp_retrans --;
1010			(void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
1011			tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_xpd_ticks);
1012		}
1013	}
1014;
1015
1016/* TP4 only */
1017SAME 			<==		TP_OPEN									TM_data_retrans
1018	( $$.e_retrans > 0 )
1019	{
1020		register 	SeqNum			low, lowsave = 0;
1021		register	struct tp_rtc 	*r = $P.tp_snduna_rtc;
1022		register	struct mbuf 	*m;
1023		register	SeqNum			high = $$.e_high;
1024
1025		low = $P.tp_snduna;
1026		lowsave = high = low;
1027
1028		tp_euntimeout_lss($P.tp_refp, TM_data_retrans,
1029			SEQ_ADD($P, $P.tp_sndhiwat, 1));
1030		$P.tp_retrans_hiwat = $P.tp_sndhiwat;
1031
1032		if (($P.tp_rx_strat & TPRX_EACH) == 0)
1033			high = (high>low)?low:high;
1034
1035		if( $P.tp_rx_strat & TPRX_USE_CW ) {
1036			register int i;
1037
1038			$P.tp_cong_win = 1;
1039			$P.tp_ackrcvd = 0;
1040			i = SEQ_ADD($P, low, $P.tp_cong_win);
1041
1042			high = SEQ_MIN($P, high, $P.tp_sndhiwat);
1043
1044		}
1045
1046		while( SEQ_LEQ($P, low, high) ){
1047			if ( r == (struct tp_rtc *)0 ){
1048				IFDEBUG(D_RTC)
1049					printf( "tp: retrans rtc list is GONE!\n");
1050				ENDDEBUG
1051				break;
1052			}
1053			if ( r->tprt_seq == low ){
1054				if(( m = m_copy(r->tprt_data, 0, r->tprt_octets ))== MNULL)
1055					break;
1056				(void) tp_emit(DT_TPDU_type, $P, low, r->tprt_eot, m);
1057				IncStat(ts_retrans_dt);
1058				SEQ_INC($P, low );
1059			}
1060			r = r->tprt_next;
1061		}
1062/* CE_BIT
1063		if ( SEQ_LEQ($P, lowsave, high) ){
1064*/
1065			$$.e_retrans --;
1066			tp_etimeout($P.tp_refp, TM_data_retrans, (caddr_t)lowsave,
1067					(caddr_t)high, $$.e_retrans,
1068					($P.tp_Nretrans - $$.e_retrans) * (int)$P.tp_dt_ticks);
1069/* CE_BIT
1070		}
1071*/
1072	}
1073;
1074
1075/* TP4 only */
1076SAME	 		<==		TP_CLOSING									TM_retrans
1077	(	$P.tp_retrans > 0 )
1078	{
1079		$P.tp_retrans --;
1080		(void) tp_emit(DR_TPDU_type, $P, 0, E_TP_DR_NO_REAS, MNULL);
1081		IncStat(ts_retrans_dr);
1082		tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks);
1083	}
1084;
1085
1086/* TP4 only */
1087TP_REFWAIT 		<==		TP_CLOSING									TM_retrans
1088	DEFAULT	/* no more retrans - gave up */
1089	{
1090		$P.tp_sock->so_error = ETIMEDOUT;
1091		$P.tp_refp->tpr_state = REF_FROZEN;
1092		tp_recycle_tsuffix( $P );
1093		tp_etimeout($P.tp_refp, TM_reference, 0,0,0, (int)$P.tp_refer_ticks);
1094	}
1095;
1096
1097/*
1098 * The resources are kept around until the ref timer goes off.
1099 * The suffices are wiped out sooner so they can be reused right away.
1100 */
1101/* applicable in TP4, TP0 */
1102TP_CLOSED 		<==		TP_REFWAIT 									TM_reference
1103	DEFAULT
1104	{
1105		tp_freeref($P.tp_refp);
1106		tp_detach($P);
1107	}
1108;
1109
1110/* applicable in TP4, TP0 */
1111/* A duplicate CR from connectionless network layer can't happen */
1112SAME 			<== 	TP_OPEN 							[ CR_TPDU, CC_TPDU ]
1113	DEFAULT
1114	{
1115		if( $P.tp_class != TP_CLASS_0) {
1116			tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
1117			if ( $E.ev_number == CC_TPDU )
1118				(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
1119		}
1120		/* ignore it if class 0 - state tables are blank for this */
1121	}
1122;
1123
1124/* applicable in TP4, TP0 */
1125SAME			<== 	TP_OPEN									T_DATA_req
1126	DEFAULT
1127	{
1128		IFTRACE(D_DATA)
1129			tptrace(TPPTmisc, "T_DATA_req sndhiwat snduna fcredit, tpcb",
1130				$P.tp_sndhiwat, $P.tp_snduna, $P.tp_fcredit, $P);
1131		ENDTRACE
1132
1133		tp_send($P);
1134	}
1135;
1136
1137/* TP4 only */
1138SAME			<==		TP_OPEN										T_XPD_req
1139	DEFAULT
1140		/* T_XPD_req was issued by sosend iff xpd socket buf was empty
1141		 * at time of sosend(),
1142		 * AND (which means) there were no unacknowledged XPD tpdus outstanding!
1143		 */
1144	{
1145		int error = 0;
1146
1147		/* resume XPD */
1148		if	( $P.tp_Xsnd.sb_mb )  {
1149			struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc);
1150			/* m_copy doesn't preserve the m_xlink field, but at this pt.
1151			 * that doesn't matter
1152			 */
1153
1154			IFTRACE(D_XPD)
1155				tptrace(TPPTmisc, "XPD req: Xuna Xsndnxt sndhiwat snduna",
1156					$P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndhiwat,
1157					$P.tp_snduna);
1158			ENDTRACE
1159			IFDEBUG(D_XPD)
1160				printf("T_XPD_req: sb_cc 0x%x\n", $P.tp_Xsnd.sb_cc);
1161				dump_mbuf(m, "XPD req emitting M");
1162			ENDDEBUG
1163			error =
1164				tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
1165			$P.tp_retrans = $P.tp_Nretrans;
1166			tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_xpd_ticks);
1167			SEQ_INC($P, $P.tp_Xsndnxt);
1168		}
1169		if(trick_hc)
1170			return error;
1171	}
1172;
1173
1174/* TP4, faked ack in TP0 when cons send completes */
1175SAME 			<==		TP_OPEN 									AK_TPDU
1176	( tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq)  )
1177
1178	/* tp_goodack == true means
1179	 * EITHER it actually acked something heretofore unacknowledged
1180	 * OR no news but the credit should be processed.
1181	 */
1182	{
1183		IFDEBUG(D_ACKRECV)
1184			printf("GOOD ACK seq 0x%x cdt 0x%x\n", $$.e_seq, $$.e_cdt);
1185		ENDDEBUG
1186		if( $P.tp_class != TP_CLASS_0) {
1187			tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
1188			tp_euntimeout_lss($P.tp_refp, TM_data_retrans, $$.e_seq);
1189		}
1190		sbwakeup( &$P.tp_sock->so_snd );
1191
1192		if ($P.tp_sndhiwat <= $P.tp_retrans_hiwat &&
1193			$P.tp_snduna <= $P.tp_retrans_hiwat) {
1194
1195			register    struct mbuf     *m;
1196			/* extern      struct mbuf     *m_copy(); */
1197			register    struct tp_rtc   *r;
1198			SeqNum      high, retrans, low_save;
1199
1200			high = SEQ_MIN($P, SEQ_ADD($P, $P.tp_snduna,
1201					MIN($P.tp_cong_win, $P.tp_fcredit)) - 1,
1202					$P.tp_sndhiwat);
1203			low_save = retrans = SEQ_MAX($P, SEQ_ADD($P, $P.tp_last_retrans, 1),
1204					$P.tp_snduna);
1205			for (; SEQ_LEQ($P, retrans, high); SEQ_INC($P, retrans)) {
1206
1207				for (r = $P.tp_snduna_rtc; r; r = r->tprt_next){
1208					if ( r->tprt_seq == retrans ){
1209						if(( m = m_copy(r->tprt_data, 0, r->tprt_octets ))
1210								== MNULL)
1211							break;
1212						(void) tp_emit(DT_TPDU_type, $P, retrans,
1213							r->tprt_eot, m);
1214						$P.tp_last_retrans = retrans;
1215						IncStat(ts_retrans_dt);
1216						break;
1217					}
1218				}
1219				if ( r == (struct tp_rtc *)0 ){
1220					IFDEBUG(D_RTC)
1221						printf( "tp: retrans rtc list is GONE!\n");
1222					ENDDEBUG
1223					break;
1224				}
1225			}
1226			tp_etimeout($P.tp_refp, TM_data_retrans, (caddr_t)low_save,
1227					(caddr_t)high, $P.tp_retrans, (int)$P.tp_dt_ticks);
1228			if (SEQ_DEC($P, retrans) == $P.tp_retrans_hiwat)
1229				tp_send($P);
1230		}
1231		else {
1232			tp_send($P);
1233		}
1234		IFDEBUG(D_ACKRECV)
1235			printf("GOOD ACK new sndhiwat 0x%x\n", $P.tp_sndhiwat);
1236		ENDDEBUG
1237	}
1238;
1239
1240/* TP4, and TP0 after sending a CC or possibly a CR */
1241SAME			<==		TP_OPEN 			 						 AK_TPDU
1242	DEFAULT
1243	{
1244		IFTRACE(D_ACKRECV)
1245			tptrace(TPPTmisc, "BOGUS ACK fcc_present, tp_r_subseq e_subseq",
1246				$$.e_fcc_present, $P.tp_r_subseq, $$.e_subseq, 0);
1247		ENDTRACE
1248		if( $P.tp_class != TP_CLASS_0 ) {
1249
1250			if ( !$$.e_fcc_present ) {
1251				/* send ACK with FCC */
1252				IncStat( ts_ackreason[_ACK_FCC_] );
1253				(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 1, MNULL);
1254			}
1255			tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
1256		}
1257	}
1258;
1259
1260/* NBS(47) */
1261	/* goes in at *** */
1262		/* just so happens that this is never true now, because we allow
1263		 * only 1 packet in the queue at once (this could be changed)
1264		if	( $P.tp_Xsnd.sb_mb )  {
1265			struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, ??);
1266
1267			(void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
1268			$P.tp_retrans = $P.tp_Nretrans;
1269			tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_xpd_ticks);
1270			SEQ_INC($P, $P.tp_Xsndnxt);
1271		}
1272		 */
1273	/* end of the above hack */
1274
1275/* TP4 only */
1276SAME			<== 	TP_OPEN										XAK_TPDU
1277	( tp_goodXack($P, $$.e_seq) )
1278	/* tp_goodXack checks for good ack, removes the correct
1279	 * tpdu from the queue and  returns 1 if ack was legit, 0 if not.
1280	 * also updates tp_Xuna
1281	 */
1282	{
1283		tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
1284		tp_cuntimeout($P.tp_refp, TM_retrans);
1285
1286		sbwakeup( &$P.tp_sock->so_snd );
1287
1288		/* resume normal data */
1289		tp_send($P);
1290	}
1291;
1292
1293/* TP4, and TP0 after sending a CC or possibly a CR */
1294SAME			<==		TP_OPEN 			 						XAK_TPDU
1295	DEFAULT
1296	{
1297		IFTRACE(D_ACKRECV)
1298			tptrace(TPPTmisc, "BOGUS XACK eventtype ", $E.ev_number, 0, 0,0);
1299		ENDTRACE
1300		if( $P.tp_class != TP_CLASS_0 ) {
1301			tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks);
1302		}
1303	}
1304;
1305
1306/* TP4 only */
1307SAME			<==		TP_OPEN 								TM_sendack
1308	DEFAULT
1309	{
1310		IFTRACE(D_TIMER)
1311			tptrace(TPPTsendack, -1, $P.tp_lcredit, $P.tp_sent_uwe,
1312			$P.tp_sent_lcdt, 0);
1313		ENDTRACE
1314		IncPStat($P, tps_n_TMsendack);
1315		(void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
1316	}
1317;
1318
1319/* TP0 only */
1320SAME			<==		TP_OPEN 									T_USR_rcvd
1321	($P.tp_class == TP_CLASS_0)
1322	NULLACTION
1323;
1324
1325/* TP4 only */
1326		/* If old credit was zero,
1327		 * we'd better inform other side that we now have space
1328		 * But this is not enough.  Sender might not yet have
1329		 * seen an ack with cdt 0 but it might still think the
1330		 * window is closed, so it's going to wait.
1331		 * Best to send an ack each time.
1332		 * Strictly speaking, this ought to be a function of the
1333		 * general ack strategy.
1334		 */
1335SAME			<==		TP_OPEN 									T_USR_rcvd
1336	DEFAULT
1337	{
1338		if( trick_hc ) {
1339			IncStat(ts_ackreason[_ACK_USRRCV_]);
1340
1341			/* send an ACK only if there's new information */
1342			LOCAL_CREDIT( $P );
1343			if (($P.tp_rcvnxt != $P.tp_sent_rcvnxt) ||
1344				($P.tp_lcredit != $P.tp_sent_lcdt))
1345
1346				return tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL);
1347		}
1348	}
1349;
1350
1351/* applicable in TP4, TP0 */
1352SAME			<==		TP_REFWAIT 				[ T_USR_rcvd, T_USR_Xrcvd ]
1353	DEFAULT
1354	/* This happens if other end sent a DR when  the user was waiting
1355	 * on a receive.
1356	 * Processing the DR includes putting us in REFWAIT state.
1357	 */
1358	{
1359		if(trick_hc)
1360		return ECONNABORTED;
1361	}
1362;
1363
1364/* TP0 only */
1365TP_REFWAIT		<==		[ TP_OPEN, TP_CRSENT, TP_LISTENING ] 	T_NETRESET
1366	( $P.tp_class != TP_CLASS_4 )
1367		/* 0 or (4 and 0) */
1368		/* in OPEN class will be 0 or 4 but not both */
1369		/* in CRSENT or LISTENING it could be in negotiation, hence both */
1370		/* Actually, this shouldn't ever happen in LISTENING */
1371	{
1372		ASSERT( $P.tp_state != TP_LISTENING );
1373		tp_indicate(T_DISCONNECT, $P, ECONNRESET);
1374		tp_soisdisconnected($P);
1375	}
1376;
1377
1378/* TP4: ignore resets */
1379SAME		<==		[ TP_OPEN, TP_CRSENT, TP_AKWAIT,
1380						TP_CLOSING, TP_LISTENING ] 				T_NETRESET
1381	DEFAULT
1382	NULLACTION
1383;
1384
1385/* applicable in TP4, TP0 */
1386SAME			<==		[ TP_CLOSED, TP_REFWAIT ]				T_NETRESET
1387	DEFAULT
1388	NULLACTION
1389;
1390
1391/* C'EST TOUT */
1392