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.2 2003/06/17 04:28:49 dillon Exp $
31 * ATM Forum UNI Support
32 * ---------------------
38 #include <netatm/kern_include.h>
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>
48 static int sscop_proc_xmit __P((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(m, pelem)
70 * Get to start of element
72 * Note that we always ensure that the current buffer has
73 * at least one byte of the next element.
75 KB_DATASTART(m, cp, caddr_t);
78 * See how much of element is in this buffer
80 if (KB_LEN(m) >= sizeof(sscop_seq)) {
82 * Get element from this buffer
84 if ((int)cp & (sizeof(sscop_seq) - 1))
85 KM_COPY(cp, (caddr_t)pelem, sizeof(sscop_seq));
87 *pelem = *(sscop_seq *)cp;
90 * Update buffer controls
92 KB_HEADADJ(m, -sizeof(sscop_seq));
95 * Get element split between two buffers
100 * Copy what's in this buffer
103 KM_COPY(cp, (caddr_t)pelem, i);
107 * Now get to next buffer
109 while (m && (KB_LEN(m) == 0))
113 * And copy remainder of element
115 j = sizeof(sscop_seq) - i;
116 KB_DATASTART(m, cp, caddr_t);
117 KM_COPY(cp, (caddr_t)pelem + i, j);
120 * Update buffer controls
126 * Put element (sequence number) into host order
131 * Get pointers set for next call
133 while (m && (KB_LEN(m) == 0))
141 * Locate SD PDU on Pending Ack Queue
144 * sop pointer to sscop connection block
145 * seq sequence number of PDU to locate
148 * addr pointer to located PDU header
149 * 0 SD PDU sequence number not found
153 sscop_pack_locate(sop, seq)
160 * Loop thru queue until we either find the PDU or the queue's
161 * sequence numbers are greater than the PDU's sequence number,
162 * indicating that the PDU is not on the queue.
164 for (php = sop->so_pack_hd; php; php = php->ph_pack_lk) {
165 if (php->ph_ns == seq)
168 if (SEQ_GT(php->ph_ns, seq, sop->so_ack)) {
179 * Free Acknowledged SD PDU
182 * sop pointer to sscop connection block
183 * seq sequence number of PDU to free
190 sscop_pack_free(sop, seq)
194 struct pdu_hdr *php, *prev;
197 * Unlink PDU from pending ack queue
199 * First, check for an empty queue
201 php = sop->so_pack_hd;
206 * Now check for PDU at head of queue
208 if (php->ph_ns == seq) {
209 if ((sop->so_pack_hd = php->ph_pack_lk) == NULL)
210 sop->so_pack_tl = NULL;
215 * Otherwise, loop thru queue until we either find the PDU or
216 * the queue's sequence numbers are greater than the PDU's
217 * sequence number, indicating that the PDU is not on the queue.
220 php = php->ph_pack_lk;
222 if (php->ph_ns == seq) {
223 if ((prev->ph_pack_lk = php->ph_pack_lk) == NULL)
224 sop->so_pack_tl = prev;
228 if (SEQ_GT(php->ph_ns, seq, sop->so_ack))
232 php = php->ph_pack_lk;
239 * We've got the ack'ed PDU - unlink it from retransmit queue
241 sscop_rexmit_unlink(sop, php);
246 KB_FREEALL(php->ph_buf);
253 * Insert SD PDU into Retransmit Queue
256 * sop pointer to sscop connection block
257 * php pointer to SD PDU header
264 sscop_rexmit_insert(sop, php)
268 struct pdu_hdr *curr, *next;
269 sscop_seq seq = php->ph_ns;
272 * Check for an empty queue
274 if ((curr = sop->so_rexmit_hd) == NULL) {
275 php->ph_rexmit_lk = NULL;
276 sop->so_rexmit_hd = php;
277 sop->so_rexmit_tl = php;
282 * Now see if PDU belongs at head of queue
284 if (SEQ_LT(seq, curr->ph_ns, sop->so_ack)) {
285 php->ph_rexmit_lk = curr;
286 sop->so_rexmit_hd = php;
291 * Otherwise, loop thru the queue until we find the
292 * proper insertion point for the PDU
294 while ((next = curr->ph_rexmit_lk) != NULL) {
295 if (SEQ_LT(seq, next->ph_ns, sop->so_ack)) {
296 php->ph_rexmit_lk = next;
297 curr->ph_rexmit_lk = php;
304 * Insert PDU at end of queue
306 php->ph_rexmit_lk = NULL;
307 curr->ph_rexmit_lk = php;
308 sop->so_rexmit_tl = php;
315 * Unlink SD PDU from Retransmit Queue
318 * sop pointer to sscop connection block
319 * php pointer to PDU header to unlink
326 sscop_rexmit_unlink(sop, php)
330 struct pdu_hdr *curr;
333 * See if PDU is on retransmit queue
335 if ((php->ph_rexmit_lk == NULL) && (sop->so_rexmit_tl != php))
339 * It's here somewhere, so first check for the PDU at the
342 if (php == sop->so_rexmit_hd) {
343 if ((sop->so_rexmit_hd = php->ph_rexmit_lk) == NULL)
344 sop->so_rexmit_tl = NULL;
345 php->ph_rexmit_lk = NULL;
350 * Otherwise, loop thru the queue until we find the PDU
352 for (curr = sop->so_rexmit_hd; curr; curr = curr->ph_rexmit_lk) {
353 if (curr->ph_rexmit_lk == php)
357 if ((curr->ph_rexmit_lk = php->ph_rexmit_lk) == NULL)
358 sop->so_rexmit_tl = curr;
361 "sscop_rexmit_unlink: Not found - sop=%p, php=%p\n",
364 panic("sscop_rexmit_unlink: Not found");
367 php->ph_rexmit_lk = NULL;
374 * Drain Transmission Queues
377 * sop pointer to sscop connection block
384 sscop_xmit_drain(sop)
391 * Free transmission queue buffers
393 while ((m = sop->so_xmit_hd) != NULL) {
394 sop->so_xmit_hd = KB_QNEXT(m);
397 sop->so_xmit_tl = NULL;
400 * Free retransmission queue
402 * All retranmission buffers are also on the pending ack
403 * queue (but not the converse), so we just clear the queue
404 * pointers here and do all the real work below.
406 sop->so_rexmit_hd = NULL;
407 sop->so_rexmit_tl = NULL;
410 * Free pending ack queue buffers
412 while ((php = sop->so_pack_hd) != NULL) {
413 sop->so_pack_hd = php->ph_pack_lk;
414 KB_FREEALL(php->ph_buf);
416 sop->so_pack_tl = NULL;
419 * Clear service required flag
421 sop->so_flags &= ~SOF_XMITSRVC;
428 * Insert SD PDU into Receive Queue
431 * sop pointer to sscop connection block
432 * php pointer to SD PDU header
435 * 0 PDU successfully inserted into queue
436 * 1 duplicate sequence number PDU on queue, PDU not inserted
440 sscop_recv_insert(sop, php)
444 struct pdu_hdr *curr, *next;
445 sscop_seq seq = php->ph_ns;
448 * Check for an empty queue
450 if ((curr = sop->so_recv_hd) == NULL) {
451 php->ph_recv_lk = NULL;
452 sop->so_recv_hd = php;
453 sop->so_recv_tl = php;
458 * Now see if PDU belongs at head of queue
460 if (SEQ_LT(seq, curr->ph_ns, sop->so_rcvnext)) {
461 php->ph_recv_lk = curr;
462 sop->so_recv_hd = php;
467 * Otherwise, loop thru the queue until we find the
468 * proper insertion point for the PDU. We also check
469 * to make sure there isn't a PDU already on the queue
470 * with a matching sequence number.
472 while ((next = curr->ph_recv_lk) != NULL) {
473 if (SEQ_LT(seq, next->ph_ns, sop->so_rcvnext)) {
474 if (seq == curr->ph_ns)
476 php->ph_recv_lk = next;
477 curr->ph_recv_lk = php;
484 * Insert PDU at end of queue
486 if (seq == curr->ph_ns)
488 php->ph_recv_lk = NULL;
489 curr->ph_recv_lk = php;
490 sop->so_recv_tl = php;
497 * Drain Receiver Queues
500 * sop pointer to sscop connection block
507 sscop_rcvr_drain(sop)
513 * Free receive queue buffers
515 while ((php = sop->so_recv_hd) != NULL) {
516 sop->so_recv_hd = php->ph_recv_lk;
517 KB_FREEALL(php->ph_buf);
519 sop->so_recv_tl = NULL;
526 * Service connection's transmit queues
529 * sop pointer to sscop connection block
536 sscop_service_xmit(sop)
541 int err = 0, pollsent = 0;
544 * Initially assume we need service
546 sop->so_flags |= SOF_XMITSRVC;
549 * Loop until done with queues
551 * (Congestion control will be added later)
554 if ((php = sop->so_rexmit_hd) != NULL) {
557 * Send SD PDU from retransmit queue
559 * First, get a copy of the PDU to send
564 KB_COPY(m, 0, KB_COPYALL, n, KB_F_NOWAIT);
571 * Now pass it down the stack
573 STACK_CALL(CPCS_UNITDATA_INV, sop->so_lower,
574 sop->so_tokl, sop->so_connvc, (int)n, 0, err);
581 * PDU is on its way, so remove it from
582 * the retransmit queue
584 if (sop->so_rexmit_tl == php) {
585 sop->so_rexmit_hd = NULL;
586 sop->so_rexmit_tl = NULL;
588 sop->so_rexmit_hd = php->ph_rexmit_lk;
590 php->ph_rexmit_lk = NULL;
593 * Update PDU's poll sequence
595 php->ph_nps = sop->so_pollsend;
597 } else if (sop->so_xmit_hd) {
600 * Newly arrived data waiting to be sent.
601 * See if transmit window allows us to send it.
603 if (SEQ_LT(sop->so_send, sop->so_sendmax, sop->so_ack)){
605 * OK, send SD PDU from transmission queue
607 err = sscop_proc_xmit(sop);
612 * Can't send now, so leave idle phase.
614 if (sop->so_timer[SSCOP_T_IDLE] != 0) {
615 sop->so_timer[SSCOP_T_IDLE] = 0;
616 sop->so_timer[SSCOP_T_NORESP] =
617 sop->so_parm.sp_timeresp;
626 * We're finished, so clear service required flag
628 sop->so_flags &= ~SOF_XMITSRVC;
633 * We've sent another SD PDU
638 * Transition into active (polling) phase
640 if (sop->so_timer[SSCOP_T_POLL] != 0) {
641 if (sop->so_flags & SOF_KEEPALIVE) {
643 * Leaving transient phase
645 sop->so_flags &= ~SOF_KEEPALIVE;
646 sop->so_timer[SSCOP_T_POLL] =
647 sop->so_parm.sp_timepoll;
653 sop->so_timer[SSCOP_T_IDLE] = 0;
654 sop->so_timer[SSCOP_T_NORESP] =
655 sop->so_parm.sp_timeresp;
656 sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timepoll;
660 * Let's see if we need to send a POLL yet
662 if (sop->so_polldata < sop->so_parm.sp_maxpd)
666 * Yup, send another poll out
668 SEQ_INCR(sop->so_pollsend, 1);
669 (void) sscop_send_poll(sop);
673 * Reset data counter for this poll cycle
675 sop->so_polldata = 0;
678 * Restart polling timer in active phase
680 sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timepoll;
684 * If we need/want to send a poll, but haven't sent any yet
685 * on this servicing, send one now
687 if (err && (pollsent == 0)) {
691 SEQ_INCR(sop->so_pollsend, 1);
692 (void) sscop_send_poll(sop);
695 * Reset data counter for this poll cycle
697 sop->so_polldata = 0;
700 * Restart polling timer in active phase
702 sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timepoll;
703 sop->so_flags &= ~SOF_KEEPALIVE;
711 * Process Transmission Queue PDU
713 * For the first entry on the transmission queue: add a PDU header and
714 * trailer, send a copy of the PDU down the stack and move the PDU from
715 * the transmission queue to the pending ack queue.
718 * sop pointer to sscop connection block
721 * 0 head of transmission queue successfully processed
722 * else processing error, tranmission queue unchanged
733 int pad, trlen, space;
737 * Get first buffer chain on queue
739 if ((m = sop->so_xmit_hd) == NULL)
743 * Count data and get to last buffer in chain
745 for (ml = m; ; ml = KB_NEXT(ml)) {
747 if (KB_NEXT(ml) == NULL)
754 if (len > sop->so_parm.sp_maxinfo) {
755 sscop_abort(sop, "sscop: maximum data size exceeded\n");
760 * Get space for PDU header
762 KB_HEADROOM(m, space);
763 if (space < sizeof(struct pdu_hdr)) {
765 * Allocate & link buffer for header
767 KB_ALLOC(n, sizeof(struct pdu_hdr), KB_F_NOWAIT, KB_T_HEADER);
772 KB_HEADSET(n, sizeof(struct pdu_hdr));
774 KB_QNEXT(n) = KB_QNEXT(m);
777 if (sop->so_xmit_tl == m)
783 * Figure out how much padding we'll need
785 pad = ((len + (PDU_PAD_ALIGN - 1)) & ~(PDU_PAD_ALIGN - 1)) - len;
786 trlen = pad + sizeof(struct sd_pdu);
789 * Now get space for PDU trailer and padding
791 KB_TAILROOM(ml, space);
794 * Allocate & link buffer for pad and trailer
796 KB_ALLOC(n, trlen, KB_F_NOWAIT, KB_T_HEADER);
806 * Build the PDU trailer
808 * Since we can't be sure of alignment in the buffers, we
809 * have to move this a byte at a time and we have to be
810 * careful with host byte order issues.
812 KB_DATASTART(ml, cp, u_char *);
813 cp += KB_LEN(ml) + pad;
814 *cp++ = (pad << PT_PAD_SHIFT) | PT_SD;
816 *(cp + 2) = (u_char)(seq & 0xff);
818 *(cp + 1) = (u_char)(seq & 0xff);
820 *(cp) = (u_char)(seq & 0xff);
824 * Get a copy of the SD PDU to send
830 KB_COPY(n, 0, KB_COPYALL, n, KB_F_NOWAIT);
837 * Now pass copy down the stack
839 STACK_CALL(CPCS_UNITDATA_INV, sop->so_lower, sop->so_tokl,
840 sop->so_connvc, (int)n, 0, err);
848 * PDU is on its way, so remove buffer from
849 * the transmission queue
851 if (sop->so_xmit_tl == m) {
852 sop->so_xmit_hd = NULL;
853 sop->so_xmit_tl = NULL;
855 sop->so_xmit_hd = KB_QNEXT(m);
862 * We can at least assume/require that the start of
863 * the user data is aligned. Also note that we don't
864 * include this header in the buffer len/offset fields.
866 KB_DATASTART(m, php, struct pdu_hdr *);
868 php->ph_ns = sop->so_send;
869 php->ph_nps = sop->so_pollsend;
871 php->ph_rexmit_lk = NULL;
872 php->ph_pack_lk = NULL;
875 * Put PDU onto the pending ack queue
877 if (sop->so_pack_hd == NULL)
878 sop->so_pack_hd = php;
880 sop->so_pack_tl->ph_pack_lk = php;
881 sop->so_pack_tl = php;
884 * Finally, bump send sequence number
886 SEQ_INCR(sop->so_send, 1);
893 * Detect Retransmitted PDUs
896 * sop pointer to sscop connection block
897 * nsq connection sequence value (N(SQ)) from received PDU
900 * 0 received PDU was NOT retransmitted
901 * 1 received PDU was retransmitted
905 sscop_is_rexmit(sop, nsq)
911 * For Q.SAAL1, N(SQ) doesn't exist
913 if (sop->so_vers == SSCOP_VERS_QSAAL)
917 * If we've already received the N(SQ) value,
918 * then this PDU has been retransmitted
920 if (nsq == sop->so_rcvconn)
924 * New PDU, save its N(SQ)
926 sop->so_rcvconn = nsq;
933 * Start connection poll timer
936 * sop pointer to sscop connection block
948 * Decide which polling timer value to set
950 if ((sop->so_xmit_hd != NULL) || SEQ_NEQ(sop->so_send, sop->so_ack)) {
952 * Data outstanding, poll frequently
954 sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timepoll;
955 sop->so_flags &= ~SOF_KEEPALIVE;
958 * No data outstanding, just poll occassionally
960 sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timekeep;
961 sop->so_flags |= SOF_KEEPALIVE;