3 * ===================================
4 * HARP | Host ATM Research Platform
5 * ===================================
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.
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.
21 * Copyright 1994-1998 Network Computing Services, Inc.
23 * Copies of this Software may be made, however, the above copyright
24 * notice must be reproduced on all copies.
26 * @(#) $FreeBSD: src/sys/netatm/uni/sscop_subr.c,v 1.6 2000/01/17 20:49:52 mks Exp $
27 * @(#) $DragonFly: src/sys/netproto/atm/uni/sscop_subr.c,v 1.8 2006/01/14 13:36:39 swildner Exp $
31 * ATM Forum UNI Support
32 * ---------------------
38 #include <netproto/atm/kern_include.h>
41 #include "sscop_misc.h"
42 #include "sscop_pdu.h"
43 #include "sscop_var.h"
48 static int sscop_proc_xmit (struct sscop
*);
52 * Get Next Element from STAT PDU
55 * m pointer to current buffer in STAT PDU
56 * pelem pointer to location to store element value
59 * addr pointer to updated current buffer in STAT PDU
63 sscop_stat_getelem(KBuffer
*m
, sscop_seq
*pelem
)
68 * Get to start of element
70 * Note that we always ensure that the current buffer has
71 * at least one byte of the next element.
73 KB_DATASTART(m
, cp
, caddr_t
);
76 * See how much of element is in this buffer
78 if (KB_LEN(m
) >= sizeof(sscop_seq
)) {
80 * Get element from this buffer
82 if ((int)cp
& (sizeof(sscop_seq
) - 1))
83 KM_COPY(cp
, (caddr_t
)pelem
, sizeof(sscop_seq
));
85 *pelem
= *(sscop_seq
*)cp
;
88 * Update buffer controls
90 KB_HEADADJ(m
, -sizeof(sscop_seq
));
93 * Get element split between two buffers
98 * Copy what's in this buffer
101 KM_COPY(cp
, (caddr_t
)pelem
, i
);
105 * Now get to next buffer
107 while (m
&& (KB_LEN(m
) == 0))
111 * And copy remainder of element
113 j
= sizeof(sscop_seq
) - i
;
114 KB_DATASTART(m
, cp
, caddr_t
);
115 KM_COPY(cp
, (caddr_t
)pelem
+ i
, j
);
118 * Update buffer controls
124 * Put element (sequence number) into host order
126 *pelem
= ntohl(*pelem
);
129 * Get pointers set for next call
131 while (m
&& (KB_LEN(m
) == 0))
139 * Locate SD PDU on Pending Ack Queue
142 * sop pointer to sscop connection block
143 * seq sequence number of PDU to locate
146 * addr pointer to located PDU header
147 * 0 SD PDU sequence number not found
151 sscop_pack_locate(struct sscop
*sop
, sscop_seq seq
)
156 * Loop thru queue until we either find the PDU or the queue's
157 * sequence numbers are greater than the PDU's sequence number,
158 * indicating that the PDU is not on the queue.
160 for (php
= sop
->so_pack_hd
; php
; php
= php
->ph_pack_lk
) {
161 if (php
->ph_ns
== seq
)
164 if (SEQ_GT(php
->ph_ns
, seq
, sop
->so_ack
)) {
175 * Free Acknowledged SD PDU
178 * sop pointer to sscop connection block
179 * seq sequence number of PDU to free
186 sscop_pack_free(struct sscop
*sop
, sscop_seq seq
)
188 struct pdu_hdr
*php
, *prev
;
191 * Unlink PDU from pending ack queue
193 * First, check for an empty queue
195 php
= sop
->so_pack_hd
;
200 * Now check for PDU at head of queue
202 if (php
->ph_ns
== seq
) {
203 if ((sop
->so_pack_hd
= php
->ph_pack_lk
) == NULL
)
204 sop
->so_pack_tl
= NULL
;
209 * Otherwise, loop thru queue until we either find the PDU or
210 * the queue's sequence numbers are greater than the PDU's
211 * sequence number, indicating that the PDU is not on the queue.
214 php
= php
->ph_pack_lk
;
216 if (php
->ph_ns
== seq
) {
217 if ((prev
->ph_pack_lk
= php
->ph_pack_lk
) == NULL
)
218 sop
->so_pack_tl
= prev
;
222 if (SEQ_GT(php
->ph_ns
, seq
, sop
->so_ack
))
226 php
= php
->ph_pack_lk
;
233 * We've got the ack'ed PDU - unlink it from retransmit queue
235 sscop_rexmit_unlink(sop
, php
);
240 KB_FREEALL(php
->ph_buf
);
247 * Insert SD PDU into Retransmit Queue
250 * sop pointer to sscop connection block
251 * php pointer to SD PDU header
258 sscop_rexmit_insert(struct sscop
*sop
, struct pdu_hdr
*php
)
260 struct pdu_hdr
*curr
, *next
;
261 sscop_seq seq
= php
->ph_ns
;
264 * Check for an empty queue
266 if ((curr
= sop
->so_rexmit_hd
) == NULL
) {
267 php
->ph_rexmit_lk
= NULL
;
268 sop
->so_rexmit_hd
= php
;
269 sop
->so_rexmit_tl
= php
;
274 * Now see if PDU belongs at head of queue
276 if (SEQ_LT(seq
, curr
->ph_ns
, sop
->so_ack
)) {
277 php
->ph_rexmit_lk
= curr
;
278 sop
->so_rexmit_hd
= php
;
283 * Otherwise, loop thru the queue until we find the
284 * proper insertion point for the PDU
286 while ((next
= curr
->ph_rexmit_lk
) != NULL
) {
287 if (SEQ_LT(seq
, next
->ph_ns
, sop
->so_ack
)) {
288 php
->ph_rexmit_lk
= next
;
289 curr
->ph_rexmit_lk
= php
;
296 * Insert PDU at end of queue
298 php
->ph_rexmit_lk
= NULL
;
299 curr
->ph_rexmit_lk
= php
;
300 sop
->so_rexmit_tl
= php
;
307 * Unlink SD PDU from Retransmit Queue
310 * sop pointer to sscop connection block
311 * php pointer to PDU header to unlink
318 sscop_rexmit_unlink(struct sscop
*sop
, struct pdu_hdr
*php
)
320 struct pdu_hdr
*curr
;
323 * See if PDU is on retransmit queue
325 if ((php
->ph_rexmit_lk
== NULL
) && (sop
->so_rexmit_tl
!= php
))
329 * It's here somewhere, so first check for the PDU at the
332 if (php
== sop
->so_rexmit_hd
) {
333 if ((sop
->so_rexmit_hd
= php
->ph_rexmit_lk
) == NULL
)
334 sop
->so_rexmit_tl
= NULL
;
335 php
->ph_rexmit_lk
= NULL
;
340 * Otherwise, loop thru the queue until we find the PDU
342 for (curr
= sop
->so_rexmit_hd
; curr
; curr
= curr
->ph_rexmit_lk
) {
343 if (curr
->ph_rexmit_lk
== php
)
347 if ((curr
->ph_rexmit_lk
= php
->ph_rexmit_lk
) == NULL
)
348 sop
->so_rexmit_tl
= curr
;
351 "sscop_rexmit_unlink: Not found - sop=%p, php=%p\n",
354 panic("sscop_rexmit_unlink: Not found");
357 php
->ph_rexmit_lk
= NULL
;
364 * Drain Transmission Queues
367 * sop pointer to sscop connection block
374 sscop_xmit_drain(struct sscop
*sop
)
380 * Free transmission queue buffers
382 while ((m
= sop
->so_xmit_hd
) != NULL
) {
383 sop
->so_xmit_hd
= KB_QNEXT(m
);
386 sop
->so_xmit_tl
= NULL
;
389 * Free retransmission queue
391 * All retranmission buffers are also on the pending ack
392 * queue (but not the converse), so we just clear the queue
393 * pointers here and do all the real work below.
395 sop
->so_rexmit_hd
= NULL
;
396 sop
->so_rexmit_tl
= NULL
;
399 * Free pending ack queue buffers
401 while ((php
= sop
->so_pack_hd
) != NULL
) {
402 sop
->so_pack_hd
= php
->ph_pack_lk
;
403 KB_FREEALL(php
->ph_buf
);
405 sop
->so_pack_tl
= NULL
;
408 * Clear service required flag
410 sop
->so_flags
&= ~SOF_XMITSRVC
;
417 * Insert SD PDU into Receive Queue
420 * sop pointer to sscop connection block
421 * php pointer to SD PDU header
424 * 0 PDU successfully inserted into queue
425 * 1 duplicate sequence number PDU on queue, PDU not inserted
429 sscop_recv_insert(struct sscop
*sop
, struct pdu_hdr
*php
)
431 struct pdu_hdr
*curr
, *next
;
432 sscop_seq seq
= php
->ph_ns
;
435 * Check for an empty queue
437 if ((curr
= sop
->so_recv_hd
) == NULL
) {
438 php
->ph_recv_lk
= NULL
;
439 sop
->so_recv_hd
= php
;
440 sop
->so_recv_tl
= php
;
445 * Now see if PDU belongs at head of queue
447 if (SEQ_LT(seq
, curr
->ph_ns
, sop
->so_rcvnext
)) {
448 php
->ph_recv_lk
= curr
;
449 sop
->so_recv_hd
= php
;
454 * Otherwise, loop thru the queue until we find the
455 * proper insertion point for the PDU. We also check
456 * to make sure there isn't a PDU already on the queue
457 * with a matching sequence number.
459 while ((next
= curr
->ph_recv_lk
) != NULL
) {
460 if (SEQ_LT(seq
, next
->ph_ns
, sop
->so_rcvnext
)) {
461 if (seq
== curr
->ph_ns
)
463 php
->ph_recv_lk
= next
;
464 curr
->ph_recv_lk
= php
;
471 * Insert PDU at end of queue
473 if (seq
== curr
->ph_ns
)
475 php
->ph_recv_lk
= NULL
;
476 curr
->ph_recv_lk
= php
;
477 sop
->so_recv_tl
= php
;
484 * Drain Receiver Queues
487 * sop pointer to sscop connection block
494 sscop_rcvr_drain(struct sscop
*sop
)
499 * Free receive queue buffers
501 while ((php
= sop
->so_recv_hd
) != NULL
) {
502 sop
->so_recv_hd
= php
->ph_recv_lk
;
503 KB_FREEALL(php
->ph_buf
);
505 sop
->so_recv_tl
= NULL
;
512 * Service connection's transmit queues
515 * sop pointer to sscop connection block
522 sscop_service_xmit(struct sscop
*sop
)
526 int err
= 0, pollsent
= 0;
529 * Initially assume we need service
531 sop
->so_flags
|= SOF_XMITSRVC
;
534 * Loop until done with queues
536 * (Congestion control will be added later)
539 if ((php
= sop
->so_rexmit_hd
) != NULL
) {
542 * Send SD PDU from retransmit queue
544 * First, get a copy of the PDU to send
549 KB_COPY(m
, 0, KB_COPYALL
, n
, KB_F_NOWAIT
);
556 * Now pass it down the stack
558 STACK_CALL(CPCS_UNITDATA_INV
, sop
->so_lower
,
559 sop
->so_tokl
, sop
->so_connvc
, (int)n
, 0, err
);
566 * PDU is on its way, so remove it from
567 * the retransmit queue
569 if (sop
->so_rexmit_tl
== php
) {
570 sop
->so_rexmit_hd
= NULL
;
571 sop
->so_rexmit_tl
= NULL
;
573 sop
->so_rexmit_hd
= php
->ph_rexmit_lk
;
575 php
->ph_rexmit_lk
= NULL
;
578 * Update PDU's poll sequence
580 php
->ph_nps
= sop
->so_pollsend
;
582 } else if (sop
->so_xmit_hd
) {
585 * Newly arrived data waiting to be sent.
586 * See if transmit window allows us to send it.
588 if (SEQ_LT(sop
->so_send
, sop
->so_sendmax
, sop
->so_ack
)){
590 * OK, send SD PDU from transmission queue
592 err
= sscop_proc_xmit(sop
);
597 * Can't send now, so leave idle phase.
599 if (sop
->so_timer
[SSCOP_T_IDLE
] != 0) {
600 sop
->so_timer
[SSCOP_T_IDLE
] = 0;
601 sop
->so_timer
[SSCOP_T_NORESP
] =
602 sop
->so_parm
.sp_timeresp
;
611 * We're finished, so clear service required flag
613 sop
->so_flags
&= ~SOF_XMITSRVC
;
618 * We've sent another SD PDU
623 * Transition into active (polling) phase
625 if (sop
->so_timer
[SSCOP_T_POLL
] != 0) {
626 if (sop
->so_flags
& SOF_KEEPALIVE
) {
628 * Leaving transient phase
630 sop
->so_flags
&= ~SOF_KEEPALIVE
;
631 sop
->so_timer
[SSCOP_T_POLL
] =
632 sop
->so_parm
.sp_timepoll
;
638 sop
->so_timer
[SSCOP_T_IDLE
] = 0;
639 sop
->so_timer
[SSCOP_T_NORESP
] =
640 sop
->so_parm
.sp_timeresp
;
641 sop
->so_timer
[SSCOP_T_POLL
] = sop
->so_parm
.sp_timepoll
;
645 * Let's see if we need to send a POLL yet
647 if (sop
->so_polldata
< sop
->so_parm
.sp_maxpd
)
651 * Yup, send another poll out
653 SEQ_INCR(sop
->so_pollsend
, 1);
654 sscop_send_poll(sop
);
658 * Reset data counter for this poll cycle
660 sop
->so_polldata
= 0;
663 * Restart polling timer in active phase
665 sop
->so_timer
[SSCOP_T_POLL
] = sop
->so_parm
.sp_timepoll
;
669 * If we need/want to send a poll, but haven't sent any yet
670 * on this servicing, send one now
672 if (err
&& (pollsent
== 0)) {
676 SEQ_INCR(sop
->so_pollsend
, 1);
677 sscop_send_poll(sop
);
680 * Reset data counter for this poll cycle
682 sop
->so_polldata
= 0;
685 * Restart polling timer in active phase
687 sop
->so_timer
[SSCOP_T_POLL
] = sop
->so_parm
.sp_timepoll
;
688 sop
->so_flags
&= ~SOF_KEEPALIVE
;
696 * Process Transmission Queue PDU
698 * For the first entry on the transmission queue: add a PDU header and
699 * trailer, send a copy of the PDU down the stack and move the PDU from
700 * the transmission queue to the pending ack queue.
703 * sop pointer to sscop connection block
706 * 0 head of transmission queue successfully processed
707 * else processing error, tranmission queue unchanged
711 sscop_proc_xmit(struct sscop
*sop
)
717 int pad
, trlen
, space
;
721 * Get first buffer chain on queue
723 if ((m
= sop
->so_xmit_hd
) == NULL
)
727 * Count data and get to last buffer in chain
729 for (ml
= m
; ; ml
= KB_NEXT(ml
)) {
731 if (KB_NEXT(ml
) == NULL
)
738 if (len
> sop
->so_parm
.sp_maxinfo
) {
739 sscop_abort(sop
, "sscop: maximum data size exceeded\n");
744 * Get space for PDU header
746 KB_HEADROOM(m
, space
);
747 if (space
< sizeof(struct pdu_hdr
)) {
749 * Allocate & link buffer for header
751 KB_ALLOC(n
, sizeof(struct pdu_hdr
), KB_F_NOWAIT
, KB_T_HEADER
);
756 KB_HEADSET(n
, sizeof(struct pdu_hdr
));
758 KB_QNEXT(n
) = KB_QNEXT(m
);
761 if (sop
->so_xmit_tl
== m
)
767 * Figure out how much padding we'll need
769 pad
= ((len
+ (PDU_PAD_ALIGN
- 1)) & ~(PDU_PAD_ALIGN
- 1)) - len
;
770 trlen
= pad
+ sizeof(struct sd_pdu
);
773 * Now get space for PDU trailer and padding
775 KB_TAILROOM(ml
, space
);
778 * Allocate & link buffer for pad and trailer
780 KB_ALLOC(n
, trlen
, KB_F_NOWAIT
, KB_T_HEADER
);
790 * Build the PDU trailer
792 * Since we can't be sure of alignment in the buffers, we
793 * have to move this a byte at a time and we have to be
794 * careful with host byte order issues.
796 KB_DATASTART(ml
, cp
, u_char
*);
797 cp
+= KB_LEN(ml
) + pad
;
798 *cp
++ = (pad
<< PT_PAD_SHIFT
) | PT_SD
;
800 *(cp
+ 2) = (u_char
)(seq
& 0xff);
802 *(cp
+ 1) = (u_char
)(seq
& 0xff);
804 *(cp
) = (u_char
)(seq
& 0xff);
808 * Get a copy of the SD PDU to send
814 KB_COPY(n
, 0, KB_COPYALL
, n
, KB_F_NOWAIT
);
821 * Now pass copy down the stack
823 STACK_CALL(CPCS_UNITDATA_INV
, sop
->so_lower
, sop
->so_tokl
,
824 sop
->so_connvc
, (int)n
, 0, err
);
832 * PDU is on its way, so remove buffer from
833 * the transmission queue
835 if (sop
->so_xmit_tl
== m
) {
836 sop
->so_xmit_hd
= NULL
;
837 sop
->so_xmit_tl
= NULL
;
839 sop
->so_xmit_hd
= KB_QNEXT(m
);
846 * We can at least assume/require that the start of
847 * the user data is aligned. Also note that we don't
848 * include this header in the buffer len/offset fields.
850 KB_DATASTART(m
, php
, struct pdu_hdr
*);
852 php
->ph_ns
= sop
->so_send
;
853 php
->ph_nps
= sop
->so_pollsend
;
855 php
->ph_rexmit_lk
= NULL
;
856 php
->ph_pack_lk
= NULL
;
859 * Put PDU onto the pending ack queue
861 if (sop
->so_pack_hd
== NULL
)
862 sop
->so_pack_hd
= php
;
864 sop
->so_pack_tl
->ph_pack_lk
= php
;
865 sop
->so_pack_tl
= php
;
868 * Finally, bump send sequence number
870 SEQ_INCR(sop
->so_send
, 1);
877 * Detect Retransmitted PDUs
880 * sop pointer to sscop connection block
881 * nsq connection sequence value (N(SQ)) from received PDU
884 * 0 received PDU was NOT retransmitted
885 * 1 received PDU was retransmitted
889 sscop_is_rexmit(struct sscop
*sop
, u_char nsq
)
893 * For Q.SAAL1, N(SQ) doesn't exist
895 if (sop
->so_vers
== SSCOP_VERS_QSAAL
)
899 * If we've already received the N(SQ) value,
900 * then this PDU has been retransmitted
902 if (nsq
== sop
->so_rcvconn
)
906 * New PDU, save its N(SQ)
908 sop
->so_rcvconn
= nsq
;
915 * Start connection poll timer
918 * sop pointer to sscop connection block
925 sscop_set_poll(struct sscop
*sop
)
929 * Decide which polling timer value to set
931 if ((sop
->so_xmit_hd
!= NULL
) || SEQ_NEQ(sop
->so_send
, sop
->so_ack
)) {
933 * Data outstanding, poll frequently
935 sop
->so_timer
[SSCOP_T_POLL
] = sop
->so_parm
.sp_timepoll
;
936 sop
->so_flags
&= ~SOF_KEEPALIVE
;
939 * No data outstanding, just poll occassionally
941 sop
->so_timer
[SSCOP_T_POLL
] = sop
->so_parm
.sp_timekeep
;
942 sop
->so_flags
|= SOF_KEEPALIVE
;