HAMMER Utilities: MFC work to date.
[dragonfly.git] / sys / netproto / atm / uni / sscop_subr.c
bloba6d08fb0610e8e9a905f101262e7dc067d349b4e
1 /*
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 * ---------------------
34 * SSCOP - Subroutines
38 #include <netproto/atm/kern_include.h>
40 #include "sscop.h"
41 #include "sscop_misc.h"
42 #include "sscop_pdu.h"
43 #include "sscop_var.h"
46 * Local functions
48 static int sscop_proc_xmit (struct sscop *);
52 * Get Next Element from STAT PDU
54 * Arguments:
55 * m pointer to current buffer in STAT PDU
56 * pelem pointer to location to store element value
58 * Returns:
59 * addr pointer to updated current buffer in STAT PDU
62 KBuffer *
63 sscop_stat_getelem(KBuffer *m, sscop_seq *pelem)
65 caddr_t cp;
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));
84 else
85 *pelem = *(sscop_seq *)cp;
88 * Update buffer controls
90 KB_HEADADJ(m, -sizeof(sscop_seq));
91 } else {
93 * Get element split between two buffers
95 int i, j;
98 * Copy what's in this buffer
100 i = KB_LEN(m);
101 KM_COPY(cp, (caddr_t)pelem, i);
102 KB_LEN(m) = 0;
105 * Now get to next buffer
107 while (m && (KB_LEN(m) == 0))
108 m = KB_NEXT(m);
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
120 KB_HEADADJ(m, -j);
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))
132 m = KB_NEXT(m);
134 return (m);
139 * Locate SD PDU on Pending Ack Queue
141 * Arguments:
142 * sop pointer to sscop connection block
143 * seq sequence number of PDU to locate
145 * Returns:
146 * addr pointer to located PDU header
147 * 0 SD PDU sequence number not found
150 struct pdu_hdr *
151 sscop_pack_locate(struct sscop *sop, sscop_seq seq)
153 struct pdu_hdr *php;
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)
162 break;
164 if (SEQ_GT(php->ph_ns, seq, sop->so_ack)) {
165 php = NULL;
166 break;
170 return (php);
175 * Free Acknowledged SD PDU
177 * Arguments:
178 * sop pointer to sscop connection block
179 * seq sequence number of PDU to free
181 * Returns:
182 * none
185 void
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;
196 if (php == NULL)
197 return;
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;
205 goto found;
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.
213 prev = php;
214 php = php->ph_pack_lk;
215 while (php) {
216 if (php->ph_ns == seq) {
217 if ((prev->ph_pack_lk = php->ph_pack_lk) == NULL)
218 sop->so_pack_tl = prev;
219 goto found;
222 if (SEQ_GT(php->ph_ns, seq, sop->so_ack))
223 return;
225 prev = php;
226 php = php->ph_pack_lk;
229 return;
231 found:
233 * We've got the ack'ed PDU - unlink it from retransmit queue
235 sscop_rexmit_unlink(sop, php);
238 * Free PDU buffers
240 KB_FREEALL(php->ph_buf);
242 return;
247 * Insert SD PDU into Retransmit Queue
249 * Arguments:
250 * sop pointer to sscop connection block
251 * php pointer to SD PDU header
253 * Returns:
254 * none
257 void
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;
270 return;
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;
279 return;
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;
290 return;
292 curr = next;
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;
302 return;
307 * Unlink SD PDU from Retransmit Queue
309 * Arguments:
310 * sop pointer to sscop connection block
311 * php pointer to PDU header to unlink
313 * Returns:
314 * none
317 void
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))
326 return;
329 * It's here somewhere, so first check for the PDU at the
330 * head of the queue
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;
336 return;
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)
344 break;
346 if (curr) {
347 if ((curr->ph_rexmit_lk = php->ph_rexmit_lk) == NULL)
348 sop->so_rexmit_tl = curr;
349 } else {
350 log(LOG_ERR,
351 "sscop_rexmit_unlink: Not found - sop=%p, php=%p\n",
352 sop, php);
353 #ifdef DIAGNOSTIC
354 panic("sscop_rexmit_unlink: Not found");
355 #endif
357 php->ph_rexmit_lk = NULL;
359 return;
364 * Drain Transmission Queues
366 * Arguments:
367 * sop pointer to sscop connection block
369 * Returns:
370 * none
373 void
374 sscop_xmit_drain(struct sscop *sop)
376 KBuffer *m;
377 struct pdu_hdr *php;
380 * Free transmission queue buffers
382 while ((m = sop->so_xmit_hd) != NULL) {
383 sop->so_xmit_hd = KB_QNEXT(m);
384 KB_FREEALL(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;
412 return;
417 * Insert SD PDU into Receive Queue
419 * Arguments:
420 * sop pointer to sscop connection block
421 * php pointer to SD PDU header
423 * Returns:
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;
441 return (0);
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;
450 return (0);
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)
462 return (1);
463 php->ph_recv_lk = next;
464 curr->ph_recv_lk = php;
465 return (0);
467 curr = next;
471 * Insert PDU at end of queue
473 if (seq == curr->ph_ns)
474 return (1);
475 php->ph_recv_lk = NULL;
476 curr->ph_recv_lk = php;
477 sop->so_recv_tl = php;
479 return (0);
484 * Drain Receiver Queues
486 * Arguments:
487 * sop pointer to sscop connection block
489 * Returns:
490 * none
493 void
494 sscop_rcvr_drain(struct sscop *sop)
496 struct pdu_hdr *php;
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;
507 return;
512 * Service connection's transmit queues
514 * Arguments:
515 * sop pointer to sscop connection block
517 * Returns:
518 * none
521 void
522 sscop_service_xmit(struct sscop *sop)
524 KBuffer *m, *n;
525 struct pdu_hdr *php;
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)
538 while (1) {
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
546 m = php->ph_buf;
547 if (KB_LEN(m) == 0)
548 m = KB_NEXT(m);
549 KB_COPY(m, 0, KB_COPYALL, n, KB_F_NOWAIT);
550 if (n == NULL) {
551 err = 1;
552 break;
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);
560 if (err) {
561 KB_FREEALL(n);
562 break;
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;
572 } else {
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);
593 if (err)
594 break;
595 } else {
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;
603 err = 1;
605 break;
608 } else {
611 * We're finished, so clear service required flag
613 sop->so_flags &= ~SOF_XMITSRVC;
614 break;
618 * We've sent another SD PDU
620 sop->so_polldata++;
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;
634 } else {
636 * Leaving idle phase
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)
648 continue;
651 * Yup, send another poll out
653 SEQ_INCR(sop->so_pollsend, 1);
654 sscop_send_poll(sop);
655 pollsent++;
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)) {
674 * Send poll
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;
691 return;
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.
702 * Arguments:
703 * sop pointer to sscop connection block
705 * Returns:
706 * 0 head of transmission queue successfully processed
707 * else processing error, tranmission queue unchanged
710 static int
711 sscop_proc_xmit(struct sscop *sop)
713 KBuffer *m, *ml, *n;
714 struct pdu_hdr *php;
715 sscop_seq seq;
716 int len = 0, err;
717 int pad, trlen, space;
718 u_char *cp;
721 * Get first buffer chain on queue
723 if ((m = sop->so_xmit_hd) == NULL)
724 return (0);
727 * Count data and get to last buffer in chain
729 for (ml = m; ; ml = KB_NEXT(ml)) {
730 len += KB_LEN(ml);
731 if (KB_NEXT(ml) == NULL)
732 break;
736 * Verify data length
738 if (len > sop->so_parm.sp_maxinfo) {
739 sscop_abort(sop, "sscop: maximum data size exceeded\n");
740 return (1);
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);
752 if (n == NULL)
753 return (1);
755 KB_LEN(n) = 0;
756 KB_HEADSET(n, sizeof(struct pdu_hdr));
757 KB_LINKHEAD(n, m);
758 KB_QNEXT(n) = KB_QNEXT(m);
759 KB_QNEXT(m) = NULL;
760 sop->so_xmit_hd = n;
761 if (sop->so_xmit_tl == m)
762 sop->so_xmit_tl = n;
763 m = n;
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);
776 if (space < trlen) {
778 * Allocate & link buffer for pad and trailer
780 KB_ALLOC(n, trlen, KB_F_NOWAIT, KB_T_HEADER);
781 if (n == NULL)
782 return (1);
784 KB_LEN(n) = 0;
785 KB_LINK(n, ml);
786 ml = n;
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;
799 seq = sop->so_send;
800 *(cp + 2) = (u_char)(seq & 0xff);
801 seq >>= 8;
802 *(cp + 1) = (u_char)(seq & 0xff);
803 seq >>= 8;
804 *(cp) = (u_char)(seq & 0xff);
805 KB_LEN(ml) += trlen;
808 * Get a copy of the SD PDU to send
810 if (KB_LEN(m) == 0)
811 n = KB_NEXT(m);
812 else
813 n = m;
814 KB_COPY(n, 0, KB_COPYALL, n, KB_F_NOWAIT);
815 if (n == NULL) {
816 KB_LEN(ml) -= trlen;
817 return (1);
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);
825 if (err) {
826 KB_FREEALL(n);
827 KB_LEN(ml) -= trlen;
828 return (1);
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;
838 } else {
839 sop->so_xmit_hd = KB_QNEXT(m);
841 KB_QNEXT(m) = NULL;
844 * Build PDU header
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 *);
851 php--;
852 php->ph_ns = sop->so_send;
853 php->ph_nps = sop->so_pollsend;
854 php->ph_buf = m;
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;
863 else
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);
872 return (0);
877 * Detect Retransmitted PDUs
879 * Arguments:
880 * sop pointer to sscop connection block
881 * nsq connection sequence value (N(SQ)) from received PDU
883 * Returns:
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)
896 return (0);
899 * If we've already received the N(SQ) value,
900 * then this PDU has been retransmitted
902 if (nsq == sop->so_rcvconn)
903 return (1);
906 * New PDU, save its N(SQ)
908 sop->so_rcvconn = nsq;
910 return (0);
915 * Start connection poll timer
917 * Arguments:
918 * sop pointer to sscop connection block
920 * Returns:
921 * none
924 void
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;
937 } else {
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;
945 return;