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 $
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>
46 __RCSID("@(#) $FreeBSD: src/sys/netatm/uni/sscop_subr.c,v 1.6 2000/01/17 20:49:52 mks Exp $");
53 static int sscop_proc_xmit __P((struct sscop *));
57 * Get Next Element from STAT PDU
60 * m pointer to current buffer in STAT PDU
61 * pelem pointer to location to store element value
64 * addr pointer to updated current buffer in STAT PDU
68 sscop_stat_getelem(m, pelem)
75 * Get to start of element
77 * Note that we always ensure that the current buffer has
78 * at least one byte of the next element.
80 KB_DATASTART(m, cp, caddr_t);
83 * See how much of element is in this buffer
85 if (KB_LEN(m) >= sizeof(sscop_seq)) {
87 * Get element from this buffer
89 if ((int)cp & (sizeof(sscop_seq) - 1))
90 KM_COPY(cp, (caddr_t)pelem, sizeof(sscop_seq));
92 *pelem = *(sscop_seq *)cp;
95 * Update buffer controls
97 KB_HEADADJ(m, -sizeof(sscop_seq));
100 * Get element split between two buffers
105 * Copy what's in this buffer
108 KM_COPY(cp, (caddr_t)pelem, i);
112 * Now get to next buffer
114 while (m && (KB_LEN(m) == 0))
118 * And copy remainder of element
120 j = sizeof(sscop_seq) - i;
121 KB_DATASTART(m, cp, caddr_t);
122 KM_COPY(cp, (caddr_t)pelem + i, j);
125 * Update buffer controls
131 * Put element (sequence number) into host order
136 * Get pointers set for next call
138 while (m && (KB_LEN(m) == 0))
146 * Locate SD PDU on Pending Ack Queue
149 * sop pointer to sscop connection block
150 * seq sequence number of PDU to locate
153 * addr pointer to located PDU header
154 * 0 SD PDU sequence number not found
158 sscop_pack_locate(sop, seq)
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.
169 for (php = sop->so_pack_hd; php; php = php->ph_pack_lk) {
170 if (php->ph_ns == seq)
173 if (SEQ_GT(php->ph_ns, seq, sop->so_ack)) {
184 * Free Acknowledged SD PDU
187 * sop pointer to sscop connection block
188 * seq sequence number of PDU to free
195 sscop_pack_free(sop, seq)
199 struct pdu_hdr *php, *prev;
202 * Unlink PDU from pending ack queue
204 * First, check for an empty queue
206 php = sop->so_pack_hd;
211 * Now check for PDU at head of queue
213 if (php->ph_ns == seq) {
214 if ((sop->so_pack_hd = php->ph_pack_lk) == NULL)
215 sop->so_pack_tl = NULL;
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.
225 php = php->ph_pack_lk;
227 if (php->ph_ns == seq) {
228 if ((prev->ph_pack_lk = php->ph_pack_lk) == NULL)
229 sop->so_pack_tl = prev;
233 if (SEQ_GT(php->ph_ns, seq, sop->so_ack))
237 php = php->ph_pack_lk;
244 * We've got the ack'ed PDU - unlink it from retransmit queue
246 sscop_rexmit_unlink(sop, php);
251 KB_FREEALL(php->ph_buf);
258 * Insert SD PDU into Retransmit Queue
261 * sop pointer to sscop connection block
262 * php pointer to SD PDU header
269 sscop_rexmit_insert(sop, php)
273 struct pdu_hdr *curr, *next;
274 sscop_seq seq = php->ph_ns;
277 * Check for an empty queue
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;
287 * Now see if PDU belongs at head of queue
289 if (SEQ_LT(seq, curr->ph_ns, sop->so_ack)) {
290 php->ph_rexmit_lk = curr;
291 sop->so_rexmit_hd = php;
296 * Otherwise, loop thru the queue until we find the
297 * proper insertion point for the PDU
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;
309 * Insert PDU at end of queue
311 php->ph_rexmit_lk = NULL;
312 curr->ph_rexmit_lk = php;
313 sop->so_rexmit_tl = php;
320 * Unlink SD PDU from Retransmit Queue
323 * sop pointer to sscop connection block
324 * php pointer to PDU header to unlink
331 sscop_rexmit_unlink(sop, php)
335 struct pdu_hdr *curr;
338 * See if PDU is on retransmit queue
340 if ((php->ph_rexmit_lk == NULL) && (sop->so_rexmit_tl != php))
344 * It's here somewhere, so first check for the PDU at the
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;
355 * Otherwise, loop thru the queue until we find the PDU
357 for (curr = sop->so_rexmit_hd; curr; curr = curr->ph_rexmit_lk) {
358 if (curr->ph_rexmit_lk == php)
362 if ((curr->ph_rexmit_lk = php->ph_rexmit_lk) == NULL)
363 sop->so_rexmit_tl = curr;
366 "sscop_rexmit_unlink: Not found - sop=%p, php=%p\n",
369 panic("sscop_rexmit_unlink: Not found");
372 php->ph_rexmit_lk = NULL;
379 * Drain Transmission Queues
382 * sop pointer to sscop connection block
389 sscop_xmit_drain(sop)
396 * Free transmission queue buffers
398 while ((m = sop->so_xmit_hd) != NULL) {
399 sop->so_xmit_hd = KB_QNEXT(m);
402 sop->so_xmit_tl = NULL;
405 * Free retransmission queue
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.
411 sop->so_rexmit_hd = NULL;
412 sop->so_rexmit_tl = NULL;
415 * Free pending ack queue buffers
417 while ((php = sop->so_pack_hd) != NULL) {
418 sop->so_pack_hd = php->ph_pack_lk;
419 KB_FREEALL(php->ph_buf);
421 sop->so_pack_tl = NULL;
424 * Clear service required flag
426 sop->so_flags &= ~SOF_XMITSRVC;
433 * Insert SD PDU into Receive Queue
436 * sop pointer to sscop connection block
437 * php pointer to SD PDU header
440 * 0 PDU successfully inserted into queue
441 * 1 duplicate sequence number PDU on queue, PDU not inserted
445 sscop_recv_insert(sop, php)
449 struct pdu_hdr *curr, *next;
450 sscop_seq seq = php->ph_ns;
453 * Check for an empty queue
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;
463 * Now see if PDU belongs at head of queue
465 if (SEQ_LT(seq, curr->ph_ns, sop->so_rcvnext)) {
466 php->ph_recv_lk = curr;
467 sop->so_recv_hd = php;
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.
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)
481 php->ph_recv_lk = next;
482 curr->ph_recv_lk = php;
489 * Insert PDU at end of queue
491 if (seq == curr->ph_ns)
493 php->ph_recv_lk = NULL;
494 curr->ph_recv_lk = php;
495 sop->so_recv_tl = php;
502 * Drain Receiver Queues
505 * sop pointer to sscop connection block
512 sscop_rcvr_drain(sop)
518 * Free receive queue buffers
520 while ((php = sop->so_recv_hd) != NULL) {
521 sop->so_recv_hd = php->ph_recv_lk;
522 KB_FREEALL(php->ph_buf);
524 sop->so_recv_tl = NULL;
531 * Service connection's transmit queues
534 * sop pointer to sscop connection block
541 sscop_service_xmit(sop)
546 int err = 0, pollsent = 0;
549 * Initially assume we need service
551 sop->so_flags |= SOF_XMITSRVC;
554 * Loop until done with queues
556 * (Congestion control will be added later)
559 if ((php = sop->so_rexmit_hd) != NULL) {
562 * Send SD PDU from retransmit queue
564 * First, get a copy of the PDU to send
569 KB_COPY(m, 0, KB_COPYALL, n, KB_F_NOWAIT);
576 * Now pass it down the stack
578 STACK_CALL(CPCS_UNITDATA_INV, sop->so_lower,
579 sop->so_tokl, sop->so_connvc, (int)n, 0, err);
586 * PDU is on its way, so remove it from
587 * the retransmit queue
589 if (sop->so_rexmit_tl == php) {
590 sop->so_rexmit_hd = NULL;
591 sop->so_rexmit_tl = NULL;
593 sop->so_rexmit_hd = php->ph_rexmit_lk;
595 php->ph_rexmit_lk = NULL;
598 * Update PDU's poll sequence
600 php->ph_nps = sop->so_pollsend;
602 } else if (sop->so_xmit_hd) {
605 * Newly arrived data waiting to be sent.
606 * See if transmit window allows us to send it.
608 if (SEQ_LT(sop->so_send, sop->so_sendmax, sop->so_ack)){
610 * OK, send SD PDU from transmission queue
612 err = sscop_proc_xmit(sop);
617 * Can't send now, so leave idle phase.
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;
631 * We're finished, so clear service required flag
633 sop->so_flags &= ~SOF_XMITSRVC;
638 * We've sent another SD PDU
643 * Transition into active (polling) phase
645 if (sop->so_timer[SSCOP_T_POLL] != 0) {
646 if (sop->so_flags & SOF_KEEPALIVE) {
648 * Leaving transient phase
650 sop->so_flags &= ~SOF_KEEPALIVE;
651 sop->so_timer[SSCOP_T_POLL] =
652 sop->so_parm.sp_timepoll;
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;
665 * Let's see if we need to send a POLL yet
667 if (sop->so_polldata < sop->so_parm.sp_maxpd)
671 * Yup, send another poll out
673 SEQ_INCR(sop->so_pollsend, 1);
674 (void) sscop_send_poll(sop);
678 * Reset data counter for this poll cycle
680 sop->so_polldata = 0;
683 * Restart polling timer in active phase
685 sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timepoll;
689 * If we need/want to send a poll, but haven't sent any yet
690 * on this servicing, send one now
692 if (err && (pollsent == 0)) {
696 SEQ_INCR(sop->so_pollsend, 1);
697 (void) sscop_send_poll(sop);
700 * Reset data counter for this poll cycle
702 sop->so_polldata = 0;
705 * Restart polling timer in active phase
707 sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timepoll;
708 sop->so_flags &= ~SOF_KEEPALIVE;
716 * Process Transmission Queue PDU
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.
723 * sop pointer to sscop connection block
726 * 0 head of transmission queue successfully processed
727 * else processing error, tranmission queue unchanged
738 int pad, trlen, space;
742 * Get first buffer chain on queue
744 if ((m = sop->so_xmit_hd) == NULL)
748 * Count data and get to last buffer in chain
750 for (ml = m; ; ml = KB_NEXT(ml)) {
752 if (KB_NEXT(ml) == NULL)
759 if (len > sop->so_parm.sp_maxinfo) {
760 sscop_abort(sop, "sscop: maximum data size exceeded\n");
765 * Get space for PDU header
767 KB_HEADROOM(m, space);
768 if (space < sizeof(struct pdu_hdr)) {
770 * Allocate & link buffer for header
772 KB_ALLOC(n, sizeof(struct pdu_hdr), KB_F_NOWAIT, KB_T_HEADER);
777 KB_HEADSET(n, sizeof(struct pdu_hdr));
779 KB_QNEXT(n) = KB_QNEXT(m);
782 if (sop->so_xmit_tl == m)
788 * Figure out how much padding we'll need
790 pad = ((len + (PDU_PAD_ALIGN - 1)) & ~(PDU_PAD_ALIGN - 1)) - len;
791 trlen = pad + sizeof(struct sd_pdu);
794 * Now get space for PDU trailer and padding
796 KB_TAILROOM(ml, space);
799 * Allocate & link buffer for pad and trailer
801 KB_ALLOC(n, trlen, KB_F_NOWAIT, KB_T_HEADER);
811 * Build the PDU trailer
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.
817 KB_DATASTART(ml, cp, u_char *);
818 cp += KB_LEN(ml) + pad;
819 *cp++ = (pad << PT_PAD_SHIFT) | PT_SD;
821 *(cp + 2) = (u_char)(seq & 0xff);
823 *(cp + 1) = (u_char)(seq & 0xff);
825 *(cp) = (u_char)(seq & 0xff);
829 * Get a copy of the SD PDU to send
835 KB_COPY(n, 0, KB_COPYALL, n, KB_F_NOWAIT);
842 * Now pass copy down the stack
844 STACK_CALL(CPCS_UNITDATA_INV, sop->so_lower, sop->so_tokl,
845 sop->so_connvc, (int)n, 0, err);
853 * PDU is on its way, so remove buffer from
854 * the transmission queue
856 if (sop->so_xmit_tl == m) {
857 sop->so_xmit_hd = NULL;
858 sop->so_xmit_tl = NULL;
860 sop->so_xmit_hd = KB_QNEXT(m);
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.
871 KB_DATASTART(m, php, struct pdu_hdr *);
873 php->ph_ns = sop->so_send;
874 php->ph_nps = sop->so_pollsend;
876 php->ph_rexmit_lk = NULL;
877 php->ph_pack_lk = NULL;
880 * Put PDU onto the pending ack queue
882 if (sop->so_pack_hd == NULL)
883 sop->so_pack_hd = php;
885 sop->so_pack_tl->ph_pack_lk = php;
886 sop->so_pack_tl = php;
889 * Finally, bump send sequence number
891 SEQ_INCR(sop->so_send, 1);
898 * Detect Retransmitted PDUs
901 * sop pointer to sscop connection block
902 * nsq connection sequence value (N(SQ)) from received PDU
905 * 0 received PDU was NOT retransmitted
906 * 1 received PDU was retransmitted
910 sscop_is_rexmit(sop, nsq)
916 * For Q.SAAL1, N(SQ) doesn't exist
918 if (sop->so_vers == SSCOP_VERS_QSAAL)
922 * If we've already received the N(SQ) value,
923 * then this PDU has been retransmitted
925 if (nsq == sop->so_rcvconn)
929 * New PDU, save its N(SQ)
931 sop->so_rcvconn = nsq;
938 * Start connection poll timer
941 * sop pointer to sscop connection block
953 * Decide which polling timer value to set
955 if ((sop->so_xmit_hd != NULL) || SEQ_NEQ(sop->so_send, sop->so_ack)) {
957 * Data outstanding, poll frequently
959 sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timepoll;
960 sop->so_flags &= ~SOF_KEEPALIVE;
963 * No data outstanding, just poll occassionally
965 sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timekeep;
966 sop->so_flags |= SOF_KEEPALIVE;