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