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