Initial import from FreeBSD RELENG_4:
[games.git] / sys / netproto / atm / uni / sscop_subr.c
1 /*
2  *
3  * ===================================
4  * HARP  |  Host ATM Research Platform
5  * ===================================
6  *
7  *
8  * This Host ATM Research Platform ("HARP") file (the "Software") is
9  * made available by Network Computing Services, Inc. ("NetworkCS")
10  * "AS IS".  NetworkCS does not provide maintenance, improvements or
11  * support of any kind.
12  *
13  * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
14  * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
15  * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
16  * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
17  * In no event shall NetworkCS be responsible for any damages, including
18  * but not limited to consequential damages, arising from or relating to
19  * any use of the Software or related support.
20  *
21  * Copyright 1994-1998 Network Computing Services, Inc.
22  *
23  * Copies of this Software may be made, however, the above copyright
24  * notice must be reproduced on all copies.
25  *
26  *      @(#) $FreeBSD: src/sys/netatm/uni/sscop_subr.c,v 1.6 2000/01/17 20:49:52 mks Exp $
27  *
28  */
29
30 /*
31  * ATM Forum UNI Support
32  * ---------------------
33  *
34  * SSCOP - Subroutines
35  *
36  */
37
38 #include <netatm/kern_include.h>
39
40 #include <netatm/uni/sscop.h>
41 #include <netatm/uni/sscop_misc.h>
42 #include <netatm/uni/sscop_pdu.h>
43 #include <netatm/uni/sscop_var.h>
44
45 #ifndef lint
46 __RCSID("@(#) $FreeBSD: src/sys/netatm/uni/sscop_subr.c,v 1.6 2000/01/17 20:49:52 mks Exp $");
47 #endif
48
49
50 /*
51  * Local functions
52  */
53 static int sscop_proc_xmit __P((struct sscop *));
54
55
56 /*
57  * Get Next Element from STAT PDU
58  *
59  * Arguments:
60  *      m       pointer to current buffer in STAT PDU
61  *      pelem   pointer to location to store element value
62  *
63  * Returns:
64  *      addr    pointer to updated current buffer in STAT PDU
65  *
66  */
67 KBuffer *
68 sscop_stat_getelem(m, pelem)
69         KBuffer         *m;
70         sscop_seq       *pelem;
71 {
72         caddr_t         cp;
73
74         /*
75          * Get to start of element
76          *
77          * Note that we always ensure that the current buffer has
78          * at least one byte of the next element.
79          */
80         KB_DATASTART(m, cp, caddr_t);
81
82         /*
83          * See how much of element is in this buffer
84          */
85         if (KB_LEN(m) >= sizeof(sscop_seq)) {
86                 /*
87                  * Get element from this buffer
88                  */
89                 if ((int)cp & (sizeof(sscop_seq) - 1))
90                         KM_COPY(cp, (caddr_t)pelem, sizeof(sscop_seq));
91                 else
92                         *pelem = *(sscop_seq *)cp;
93
94                 /*
95                  * Update buffer controls
96                  */
97                 KB_HEADADJ(m, -sizeof(sscop_seq));
98         } else {
99                 /*
100                  * Get element split between two buffers
101                  */
102                 int     i, j;
103
104                 /*
105                  * Copy what's in this buffer
106                  */
107                 i = KB_LEN(m);
108                 KM_COPY(cp, (caddr_t)pelem, i);
109                 KB_LEN(m) = 0;
110
111                 /*
112                  * Now get to next buffer
113                  */
114                 while (m && (KB_LEN(m) == 0))
115                         m = KB_NEXT(m);
116
117                 /*
118                  * And copy remainder of element
119                  */
120                 j = sizeof(sscop_seq) - i;
121                 KB_DATASTART(m, cp, caddr_t);
122                 KM_COPY(cp, (caddr_t)pelem + i, j);
123
124                 /*
125                  * Update buffer controls
126                  */
127                 KB_HEADADJ(m, -j);
128         }
129
130         /*
131          * Put element (sequence number) into host order
132          */
133         NTOHL(*pelem);
134
135         /*
136          * Get pointers set for next call
137          */
138         while (m && (KB_LEN(m) == 0))
139                 m = KB_NEXT(m);
140
141         return (m);
142 }
143
144
145 /*
146  * Locate SD PDU on Pending Ack Queue
147  *
148  * Arguments:
149  *      sop     pointer to sscop connection block
150  *      seq     sequence number of PDU to locate
151  *
152  * Returns:
153  *      addr    pointer to located PDU header
154  *      0       SD PDU sequence number not found
155  *
156  */
157 struct pdu_hdr *
158 sscop_pack_locate(sop, seq)
159         struct sscop    *sop;
160         sscop_seq       seq;
161 {
162         struct pdu_hdr  *php;
163
164         /*
165          * Loop thru queue until we either find the PDU or the queue's
166          * sequence numbers are greater than the PDU's sequence number,
167          * indicating that the PDU is not on the queue.
168          */
169         for (php = sop->so_pack_hd; php; php = php->ph_pack_lk) {
170                 if (php->ph_ns == seq)
171                         break;
172
173                 if (SEQ_GT(php->ph_ns, seq, sop->so_ack)) {
174                         php = NULL;
175                         break;
176                 }
177         }
178
179         return (php);
180 }
181
182
183 /*
184  * Free Acknowledged SD PDU
185  *
186  * Arguments:
187  *      sop     pointer to sscop connection block
188  *      seq     sequence number of PDU to free
189  *
190  * Returns:
191  *      none
192  *
193  */
194 void
195 sscop_pack_free(sop, seq)
196         struct sscop    *sop;
197         sscop_seq       seq;
198 {
199         struct pdu_hdr  *php, *prev;
200
201         /*
202          * Unlink PDU from pending ack queue
203          *
204          * First, check for an empty queue
205          */
206         php = sop->so_pack_hd;
207         if (php == NULL)
208                 return;
209
210         /*
211          * Now check for PDU at head of queue
212          */
213         if (php->ph_ns == seq) {
214                 if ((sop->so_pack_hd = php->ph_pack_lk) == NULL)
215                         sop->so_pack_tl = NULL;
216                 goto found;
217         }
218
219         /*
220          * Otherwise, loop thru queue until we either find the PDU or
221          * the queue's sequence numbers are greater than the PDU's
222          * sequence number, indicating that the PDU is not on the queue.
223          */
224         prev = php;
225         php = php->ph_pack_lk;
226         while (php) {
227                 if (php->ph_ns == seq) {
228                         if ((prev->ph_pack_lk = php->ph_pack_lk) == NULL)
229                                 sop->so_pack_tl = prev;
230                         goto found;
231                 }
232
233                 if (SEQ_GT(php->ph_ns, seq, sop->so_ack))
234                         return;
235
236                 prev = php;
237                 php = php->ph_pack_lk;
238         }
239
240         return;
241
242 found:
243         /*
244          * We've got the ack'ed PDU - unlink it from retransmit queue
245          */
246         sscop_rexmit_unlink(sop, php);
247
248         /*
249          * Free PDU buffers
250          */
251         KB_FREEALL(php->ph_buf);
252
253         return;
254 }
255
256
257 /*
258  * Insert SD PDU into Retransmit Queue
259  *
260  * Arguments:
261  *      sop     pointer to sscop connection block
262  *      php     pointer to SD PDU header
263  *
264  * Returns:
265  *      none
266  *
267  */
268 void
269 sscop_rexmit_insert(sop, php)
270         struct sscop    *sop;
271         struct pdu_hdr  *php;
272 {
273         struct pdu_hdr  *curr, *next;
274         sscop_seq       seq = php->ph_ns;
275
276         /*
277          * Check for an empty queue
278          */
279         if ((curr = sop->so_rexmit_hd) == NULL) {
280                 php->ph_rexmit_lk = NULL;
281                 sop->so_rexmit_hd = php;
282                 sop->so_rexmit_tl = php;
283                 return;
284         }
285
286         /*
287          * Now see if PDU belongs at head of queue
288          */
289         if (SEQ_LT(seq, curr->ph_ns, sop->so_ack)) {
290                 php->ph_rexmit_lk = curr;
291                 sop->so_rexmit_hd = php;
292                 return;
293         }
294
295         /*
296          * Otherwise, loop thru the queue until we find the
297          * proper insertion point for the PDU
298          */
299         while ((next = curr->ph_rexmit_lk) != NULL) {
300                 if (SEQ_LT(seq, next->ph_ns, sop->so_ack)) {
301                         php->ph_rexmit_lk = next;
302                         curr->ph_rexmit_lk = php;
303                         return;
304                 }
305                 curr = next;
306         }
307
308         /*
309          * Insert PDU at end of queue
310          */
311         php->ph_rexmit_lk = NULL;
312         curr->ph_rexmit_lk = php;
313         sop->so_rexmit_tl = php;
314
315         return;
316 }
317
318
319 /*
320  * Unlink SD PDU from Retransmit Queue
321  *
322  * Arguments:
323  *      sop     pointer to sscop connection block
324  *      php     pointer to PDU header to unlink
325  *
326  * Returns:
327  *      none
328  *
329  */
330 void
331 sscop_rexmit_unlink(sop, php)
332         struct sscop    *sop;
333         struct pdu_hdr  *php;
334 {
335         struct pdu_hdr  *curr;
336
337         /*
338          * See if PDU is on retransmit queue
339          */
340         if ((php->ph_rexmit_lk == NULL) && (sop->so_rexmit_tl != php))
341                 return;
342
343         /*
344          * It's here somewhere, so first check for the PDU at the
345          * head of the queue
346          */
347         if (php == sop->so_rexmit_hd) {
348                 if ((sop->so_rexmit_hd = php->ph_rexmit_lk) == NULL)
349                         sop->so_rexmit_tl = NULL;
350                 php->ph_rexmit_lk = NULL;
351                 return;
352         }
353
354         /*
355          * Otherwise, loop thru the queue until we find the PDU
356          */
357         for (curr = sop->so_rexmit_hd; curr; curr = curr->ph_rexmit_lk) {
358                 if (curr->ph_rexmit_lk == php)
359                         break;
360         }
361         if (curr) {
362                 if ((curr->ph_rexmit_lk = php->ph_rexmit_lk) == NULL)
363                         sop->so_rexmit_tl = curr;
364         } else {
365                 log(LOG_ERR,
366                         "sscop_rexmit_unlink: Not found - sop=%p, php=%p\n",
367                         sop, php);
368 #ifdef DIAGNOSTIC
369                 panic("sscop_rexmit_unlink: Not found");
370 #endif
371         }
372         php->ph_rexmit_lk = NULL;
373
374         return;
375 }
376
377
378 /*
379  * Drain Transmission Queues
380  *
381  * Arguments:
382  *      sop     pointer to sscop connection block
383  *
384  * Returns:
385  *      none
386  *
387  */
388 void
389 sscop_xmit_drain(sop)
390         struct sscop    *sop;
391 {
392         KBuffer         *m;
393         struct pdu_hdr  *php;
394
395         /*
396          * Free transmission queue buffers
397          */
398         while ((m = sop->so_xmit_hd) != NULL) {
399                 sop->so_xmit_hd = KB_QNEXT(m);
400                 KB_FREEALL(m);
401         }
402         sop->so_xmit_tl = NULL;
403
404         /*
405          * Free retransmission queue
406          *
407          * All retranmission buffers are also on the pending ack
408          * queue (but not the converse), so we just clear the queue
409          * pointers here and do all the real work below.
410          */
411         sop->so_rexmit_hd = NULL;
412         sop->so_rexmit_tl = NULL;
413
414         /*
415          * Free pending ack queue buffers
416          */
417         while ((php = sop->so_pack_hd) != NULL) {
418                 sop->so_pack_hd = php->ph_pack_lk;
419                 KB_FREEALL(php->ph_buf);
420         }
421         sop->so_pack_tl = NULL;
422
423         /*
424          * Clear service required flag
425          */
426         sop->so_flags &= ~SOF_XMITSRVC;
427
428         return;
429 }
430
431
432 /*
433  * Insert SD PDU into Receive Queue
434  *
435  * Arguments:
436  *      sop     pointer to sscop connection block
437  *      php     pointer to SD PDU header
438  *
439  * Returns:
440  *      0       PDU successfully inserted into queue
441  *      1       duplicate sequence number PDU on queue, PDU not inserted
442  *
443  */
444 int
445 sscop_recv_insert(sop, php)
446         struct sscop    *sop;
447         struct pdu_hdr  *php;
448 {
449         struct pdu_hdr  *curr, *next;
450         sscop_seq       seq = php->ph_ns;
451
452         /*
453          * Check for an empty queue
454          */
455         if ((curr = sop->so_recv_hd) == NULL) {
456                 php->ph_recv_lk = NULL;
457                 sop->so_recv_hd = php;
458                 sop->so_recv_tl = php;
459                 return (0);
460         }
461
462         /*
463          * Now see if PDU belongs at head of queue
464          */
465         if (SEQ_LT(seq, curr->ph_ns, sop->so_rcvnext)) {
466                 php->ph_recv_lk = curr;
467                 sop->so_recv_hd = php;
468                 return (0);
469         }
470
471         /*
472          * Otherwise, loop thru the queue until we find the
473          * proper insertion point for the PDU.  We also check
474          * to make sure there isn't a PDU already on the queue
475          * with a matching sequence number.
476          */
477         while ((next = curr->ph_recv_lk) != NULL) {
478                 if (SEQ_LT(seq, next->ph_ns, sop->so_rcvnext)) {
479                         if (seq == curr->ph_ns)
480                                 return (1);
481                         php->ph_recv_lk = next;
482                         curr->ph_recv_lk = php;
483                         return (0);
484                 }
485                 curr = next;
486         }
487
488         /*
489          * Insert PDU at end of queue
490          */
491         if (seq == curr->ph_ns)
492                 return (1);
493         php->ph_recv_lk = NULL;
494         curr->ph_recv_lk = php;
495         sop->so_recv_tl = php;
496
497         return (0);
498 }
499
500
501 /*
502  * Drain Receiver Queues
503  *
504  * Arguments:
505  *      sop     pointer to sscop connection block
506  *
507  * Returns:
508  *      none
509  *
510  */
511 void
512 sscop_rcvr_drain(sop)
513         struct sscop    *sop;
514 {
515         struct pdu_hdr  *php;
516
517         /*
518          * Free receive queue buffers
519          */
520         while ((php = sop->so_recv_hd) != NULL) {
521                 sop->so_recv_hd = php->ph_recv_lk;
522                 KB_FREEALL(php->ph_buf);
523         }
524         sop->so_recv_tl = NULL;
525
526         return;
527 }
528
529
530 /*
531  * Service connection's transmit queues
532  *
533  * Arguments:
534  *      sop     pointer to sscop connection block
535  *
536  * Returns:
537  *      none
538  *
539  */
540 void
541 sscop_service_xmit(sop)
542         struct sscop    *sop;
543 {
544         KBuffer         *m, *n;
545         struct pdu_hdr  *php;
546         int             err = 0, pollsent = 0;
547
548         /*
549          * Initially assume we need service
550          */
551         sop->so_flags |= SOF_XMITSRVC;
552
553         /*
554          * Loop until done with queues
555          *
556          * (Congestion control will be added later)
557          */
558         while (1) {
559                 if ((php = sop->so_rexmit_hd) != NULL) {
560
561                         /*
562                          * Send SD PDU from retransmit queue
563                          *
564                          * First, get a copy of the PDU to send
565                          */
566                         m = php->ph_buf;
567                         if (KB_LEN(m) == 0)
568                                 m = KB_NEXT(m);
569                         KB_COPY(m, 0, KB_COPYALL, n, KB_F_NOWAIT);
570                         if (n == NULL) {
571                                 err = 1;
572                                 break;
573                         }
574
575                         /*
576                          * Now pass it down the stack
577                          */
578                         STACK_CALL(CPCS_UNITDATA_INV, sop->so_lower,
579                                 sop->so_tokl, sop->so_connvc, (int)n, 0, err);
580                         if (err) {
581                                 KB_FREEALL(n);
582                                 break;
583                         }
584
585                         /*
586                          * PDU is on its way, so remove it from
587                          * the retransmit queue
588                          */
589                         if (sop->so_rexmit_tl == php) {
590                                 sop->so_rexmit_hd = NULL;
591                                 sop->so_rexmit_tl = NULL;
592                         } else {
593                                 sop->so_rexmit_hd = php->ph_rexmit_lk;
594                         }
595                         php->ph_rexmit_lk = NULL;
596
597                         /*
598                          * Update PDU's poll sequence
599                          */
600                         php->ph_nps = sop->so_pollsend;
601
602                 } else if (sop->so_xmit_hd) {
603
604                         /*
605                          * Newly arrived data waiting to be sent.
606                          * See if transmit window allows us to send it.
607                          */
608                         if (SEQ_LT(sop->so_send, sop->so_sendmax, sop->so_ack)){
609                                 /*
610                                  * OK, send SD PDU from transmission queue
611                                  */
612                                 err = sscop_proc_xmit(sop);
613                                 if (err)
614                                         break;
615                         } else {
616                                 /*
617                                  * Can't send now, so leave idle phase.
618                                  */
619                                 if (sop->so_timer[SSCOP_T_IDLE] != 0) {
620                                         sop->so_timer[SSCOP_T_IDLE] = 0;
621                                         sop->so_timer[SSCOP_T_NORESP] =
622                                                 sop->so_parm.sp_timeresp;
623                                         err = 1;
624                                 }
625                                 break;
626                         }
627
628                 } else {
629
630                         /*
631                          * We're finished, so clear service required flag
632                          */
633                         sop->so_flags &= ~SOF_XMITSRVC;
634                         break;
635                 }
636
637                 /*
638                  * We've sent another SD PDU
639                  */
640                 sop->so_polldata++;
641
642                 /*
643                  * Transition into active (polling) phase
644                  */
645                 if (sop->so_timer[SSCOP_T_POLL] != 0) {
646                         if (sop->so_flags & SOF_KEEPALIVE) {
647                                 /*
648                                  * Leaving transient phase
649                                  */
650                                 sop->so_flags &= ~SOF_KEEPALIVE;
651                                 sop->so_timer[SSCOP_T_POLL] =
652                                                 sop->so_parm.sp_timepoll;
653                         }
654                 } else {
655                         /*
656                          * Leaving idle phase
657                          */
658                         sop->so_timer[SSCOP_T_IDLE] = 0;
659                         sop->so_timer[SSCOP_T_NORESP] =
660                                                 sop->so_parm.sp_timeresp;
661                         sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timepoll;
662                 }
663
664                 /*
665                  * Let's see if we need to send a POLL yet
666                  */
667                 if (sop->so_polldata < sop->so_parm.sp_maxpd)
668                         continue;
669
670                 /*
671                  * Yup, send another poll out
672                  */
673                 SEQ_INCR(sop->so_pollsend, 1);
674                 (void) sscop_send_poll(sop);
675                 pollsent++;
676
677                 /*
678                  * Reset data counter for this poll cycle
679                  */
680                 sop->so_polldata = 0;
681
682                 /*
683                  * Restart polling timer in active phase
684                  */
685                 sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timepoll;
686         }
687
688         /*
689          * If we need/want to send a poll, but haven't sent any yet
690          * on this servicing, send one now
691          */
692         if (err && (pollsent == 0)) {
693                 /*
694                  * Send poll
695                  */
696                 SEQ_INCR(sop->so_pollsend, 1);
697                 (void) sscop_send_poll(sop);
698
699                 /*
700                  * Reset data counter for this poll cycle
701                  */
702                 sop->so_polldata = 0;
703
704                 /*
705                  * Restart polling timer in active phase
706                  */
707                 sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timepoll;
708                 sop->so_flags &= ~SOF_KEEPALIVE;
709         }
710
711         return;
712 }
713
714
715 /*
716  * Process Transmission Queue PDU
717  *
718  * For the first entry on the transmission queue: add a PDU header and
719  * trailer, send a copy of the PDU down the stack and move the PDU from
720  * the transmission queue to the pending ack queue.
721  *
722  * Arguments:
723  *      sop     pointer to sscop connection block
724  *
725  * Returns:
726  *      0       head of transmission queue successfully processed
727  *      else    processing error, tranmission queue unchanged
728  *
729  */
730 static int
731 sscop_proc_xmit(sop)
732         struct sscop    *sop;
733 {
734         KBuffer         *m, *ml, *n;
735         struct pdu_hdr  *php;
736         sscop_seq       seq;
737         int             len = 0, err;
738         int             pad, trlen, space;
739         u_char          *cp;
740
741         /*
742          * Get first buffer chain on queue
743          */
744         if ((m = sop->so_xmit_hd) == NULL)
745                 return (0);
746
747         /*
748          * Count data and get to last buffer in chain
749          */
750         for (ml = m; ; ml = KB_NEXT(ml)) {
751                 len += KB_LEN(ml);
752                 if (KB_NEXT(ml) == NULL)
753                         break;
754         }
755
756         /*
757          * Verify data length
758          */
759         if (len > sop->so_parm.sp_maxinfo) {
760                 sscop_abort(sop, "sscop: maximum data size exceeded\n");
761                 return (1);
762         }
763
764         /*
765          * Get space for PDU header
766          */
767         KB_HEADROOM(m, space);
768         if (space < sizeof(struct pdu_hdr)) {
769                 /*
770                  * Allocate & link buffer for header
771                  */
772                 KB_ALLOC(n, sizeof(struct pdu_hdr), KB_F_NOWAIT, KB_T_HEADER);
773                 if (n == NULL)
774                         return (1);
775
776                 KB_LEN(n) = 0;
777                 KB_HEADSET(n, sizeof(struct pdu_hdr));
778                 KB_LINKHEAD(n, m);
779                 KB_QNEXT(n) = KB_QNEXT(m);
780                 KB_QNEXT(m) = NULL;
781                 sop->so_xmit_hd = n;
782                 if (sop->so_xmit_tl == m)
783                         sop->so_xmit_tl = n;
784                 m = n;
785         }
786
787         /*
788          * Figure out how much padding we'll need
789          */
790         pad = ((len + (PDU_PAD_ALIGN - 1)) & ~(PDU_PAD_ALIGN - 1)) - len;
791         trlen = pad + sizeof(struct sd_pdu);
792
793         /*
794          * Now get space for PDU trailer and padding
795          */
796         KB_TAILROOM(ml, space);
797         if (space < trlen) {
798                 /*
799                  * Allocate & link buffer for pad and trailer
800                  */
801                 KB_ALLOC(n, trlen, KB_F_NOWAIT, KB_T_HEADER);
802                 if (n == NULL)
803                         return (1);
804
805                 KB_LEN(n) = 0;
806                 KB_LINK(n, ml);
807                 ml = n;
808         }
809
810         /*
811          * Build the PDU trailer
812          *
813          * Since we can't be sure of alignment in the buffers, we
814          * have to move this a byte at a time and we have to be
815          * careful with host byte order issues.
816          */
817         KB_DATASTART(ml, cp, u_char *);
818         cp += KB_LEN(ml) + pad;
819         *cp++ = (pad << PT_PAD_SHIFT) | PT_SD;
820         seq = sop->so_send;
821         *(cp + 2) = (u_char)(seq & 0xff);
822         seq >>= 8;
823         *(cp + 1) = (u_char)(seq & 0xff);
824         seq >>= 8;
825         *(cp) = (u_char)(seq & 0xff);
826         KB_LEN(ml) += trlen;
827
828         /*
829          * Get a copy of the SD PDU to send
830          */
831         if (KB_LEN(m) == 0)
832                 n = KB_NEXT(m);
833         else
834                 n = m;
835         KB_COPY(n, 0, KB_COPYALL, n, KB_F_NOWAIT);
836         if (n == NULL) {
837                 KB_LEN(ml) -= trlen;
838                 return (1);
839         }
840
841         /*
842          * Now pass copy down the stack
843          */
844         STACK_CALL(CPCS_UNITDATA_INV, sop->so_lower, sop->so_tokl,
845                 sop->so_connvc, (int)n, 0, err);
846         if (err) {
847                 KB_FREEALL(n);
848                 KB_LEN(ml) -= trlen;
849                 return (1);
850         }
851
852         /*
853          * PDU is on its way, so remove buffer from
854          * the transmission queue
855          */
856         if (sop->so_xmit_tl == m) {
857                 sop->so_xmit_hd = NULL;
858                 sop->so_xmit_tl = NULL;
859         } else {
860                 sop->so_xmit_hd = KB_QNEXT(m);
861         }
862         KB_QNEXT(m) = NULL;
863
864         /*
865          * Build PDU header
866          *
867          * We can at least assume/require that the start of
868          * the user data is aligned.  Also note that we don't
869          * include this header in the buffer len/offset fields.
870          */
871         KB_DATASTART(m, php, struct pdu_hdr *);
872         php--;
873         php->ph_ns = sop->so_send;
874         php->ph_nps = sop->so_pollsend;
875         php->ph_buf = m;
876         php->ph_rexmit_lk = NULL;
877         php->ph_pack_lk = NULL;
878
879         /*
880          * Put PDU onto the pending ack queue
881          */
882         if (sop->so_pack_hd == NULL)
883                 sop->so_pack_hd = php;
884         else
885                 sop->so_pack_tl->ph_pack_lk = php;
886         sop->so_pack_tl = php;
887
888         /*
889          * Finally, bump send sequence number
890          */
891         SEQ_INCR(sop->so_send, 1);
892
893         return (0);
894 }
895
896
897 /*
898  * Detect Retransmitted PDUs
899  *
900  * Arguments:
901  *      sop     pointer to sscop connection block
902  *      nsq     connection sequence value (N(SQ)) from received PDU
903  *
904  * Returns:
905  *      0       received PDU was NOT retransmitted
906  *      1       received PDU was retransmitted
907  *
908  */
909 int
910 sscop_is_rexmit(sop, nsq)
911         struct sscop    *sop;
912         u_char          nsq;
913 {
914
915         /*
916          * For Q.SAAL1, N(SQ) doesn't exist
917          */
918         if (sop->so_vers == SSCOP_VERS_QSAAL)
919                 return (0);
920
921         /*
922          * If we've already received the N(SQ) value,
923          * then this PDU has been retransmitted
924          */
925         if (nsq == sop->so_rcvconn)
926                 return (1);
927
928         /*
929          * New PDU, save its N(SQ)
930          */
931         sop->so_rcvconn = nsq;
932
933         return (0);
934 }
935
936
937 /*
938  * Start connection poll timer
939  *
940  * Arguments:
941  *      sop     pointer to sscop connection block
942  *
943  * Returns:
944  *      none
945  *
946  */
947 void
948 sscop_set_poll(sop)
949         struct sscop    *sop;
950 {
951
952         /*
953          * Decide which polling timer value to set
954          */
955         if ((sop->so_xmit_hd != NULL) || SEQ_NEQ(sop->so_send, sop->so_ack)) {
956                 /*
957                  * Data outstanding, poll frequently
958                  */
959                 sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timepoll;
960                 sop->so_flags &= ~SOF_KEEPALIVE;
961         } else {
962                 /*
963                  * No data outstanding, just poll occassionally
964                  */
965                 sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timekeep;
966                 sop->so_flags |= SOF_KEEPALIVE;
967         }
968
969         return;
970 }
971