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