xref: /original-bsd/sys/netccitt/hd_output.c (revision 3705696b)
1 /*
2  * Copyright (c) University of British Columbia, 1984
3  * Copyright (c) 1990, 1993
4  *	The Regents of the University of California.  All rights reserved.
5  *
6  * This code is derived from software contributed to Berkeley by
7  * the Laboratory for Computation Vision and the Computer Science Department
8  * of the University of British Columbia.
9  *
10  * %sccs.include.redist.c%
11  *
12  *	@(#)hd_output.c	8.1 (Berkeley) 06/10/93
13  */
14 
15 #include <sys/param.h>
16 #include <sys/systm.h>
17 #include <sys/mbuf.h>
18 #include <sys/domain.h>
19 #include <sys/socket.h>
20 #include <sys/syslog.h>
21 #include <sys/protosw.h>
22 #include <sys/errno.h>
23 #include <sys/time.h>
24 #include <sys/kernel.h>
25 
26 #include <net/if.h>
27 
28 #include <netccitt/hdlc.h>
29 #include <netccitt/hd_var.h>
30 #include <netccitt/x25.h>
31 
32 /*
33  *      HDLC OUTPUT INTERFACE
34  *
35  *      This routine is called when the X.25 packet layer output routine
36  *      has a information frame (iframe)  to write.   It is  also called
37  *      by the input and control routines of the HDLC layer.
38  */
39 
40 hd_output (hdp, m0)
41 register struct hdcb *hdp;
42 struct mbuf *m0;
43 {
44 	struct x25config *xcp;
45 	register struct mbuf *m = m0;
46 	int len;
47 
48 	if (m == NULL)
49 		panic ("hd_output");
50 	if ((m->m_flags & M_PKTHDR) == 0)
51 		panic ("hd_output 2");
52 
53 	if (hdp->hd_state != ABM) {
54 		m_freem (m);
55 		return;
56 	}
57 
58 	/*
59 	 * Make room for the hdlc header either by prepending
60 	 * another mbuf, or by adjusting the offset and length
61 	 * of the first mbuf in the mbuf chain.
62 	 */
63 
64 	M_PREPEND(m, HDHEADERLN, M_DONTWAIT);
65 	if (m == NULL)
66 		return;
67 	for (len = 0; m; m = m->m_next)
68 		len += m->m_len;
69 	m = m0;
70 	m->m_pkthdr.len = len;
71 
72 	hd_append (&hdp->hd_txq, m);
73 	hd_start (hdp);
74 }
75 
76 hd_start (hdp)
77 register struct hdcb *hdp;
78 {
79 	register struct mbuf *m;
80 
81 	/*
82 	 * The iframe is only transmitted if all these conditions are FALSE.
83 	 * The iframe remains queued (hdp->hd_txq) however and will be
84 	 * transmitted as soon as these conditions are cleared.
85 	 */
86 
87 	while (!(hdp->hd_condition & (TIMER_RECOVERY_CONDITION | REMOTE_RNR_CONDITION | REJ_CONDITION))) {
88 		if (hdp->hd_vs == (hdp->hd_lastrxnr + hdp->hd_xcp->xc_lwsize) % MODULUS) {
89 
90 			/* We have now exceeded the  maximum  number  of
91 			   outstanding iframes. Therefore,  we must wait
92 			   until  at least  one is acknowledged if this
93 			   condition  is not  turned off before we are
94 			   requested to write another iframe. */
95 			hdp->hd_window_condition++;
96 			break;
97 		}
98 
99 		/* hd_remove top iframe from transmit queue. */
100 		if ((m = hd_remove (&hdp->hd_txq)) == NULL)
101 			break;
102 
103 		hd_send_iframe (hdp, m, POLLOFF);
104 	}
105 }
106 
107 /*
108  *  This procedure is passed a buffer descriptor for an iframe. It builds
109  *  the rest of the control part of the frame and then writes it out.  It
110  *  also  starts the  acknowledgement  timer and keeps  the iframe in the
111  *  Retransmit queue (Retxq) just in case  we have to do this again.
112  *
113  *  Note: This routine is also called from hd_input.c when retransmission
114  *       of old frames is required.
115  */
116 
117 hd_send_iframe (hdp, buf, poll_bit)
118 register struct hdcb *hdp;
119 register struct mbuf *buf;
120 int poll_bit;
121 {
122 	register struct Hdlc_iframe *iframe;
123 	struct mbuf *m;
124 
125 	KILL_TIMER (hdp);
126 
127 	if (buf == 0) {
128 		printf ("hd_send_iframe: zero arg\n");
129 #ifdef HDLCDEBUG
130 		hd_status (hdp);
131 		hd_dumptrace (hdp);
132 #endif
133 		hdp->hd_vs = (hdp->hd_vs + 7) % MODULUS;
134 		return;
135 	}
136 	iframe = mtod (buf, struct Hdlc_iframe *);
137 
138 	iframe -> hdlc_0 = 0;
139 	iframe -> nr = hdp->hd_vr;
140 	iframe -> pf = poll_bit;
141 	iframe -> ns = hdp->hd_vs;
142 	iframe -> address = ADDRESS_B;
143 	hdp->hd_lasttxnr = hdp->hd_vr;
144 	hdp->hd_rrtimer = 0;
145 
146 	if (hdp->hd_vs == hdp->hd_retxqi) {
147 		/* Check for retransmissions. */
148 		/* Put iframe only once in the Retransmission queue. */
149 		hdp->hd_retxq[hdp->hd_retxqi] = buf;
150 		hdp->hd_retxqi = (hdp->hd_retxqi + 1) % MODULUS;
151 		hdp->hd_iframes_out++;
152 	}
153 
154 	hdp->hd_vs = (hdp->hd_vs + 1) % MODULUS;
155 
156 	hd_trace (hdp, TX, (struct Hdlc_frame *)iframe);
157 
158 	/* Write buffer on device. */
159 	m = hdp->hd_dontcopy ? buf : m_copy(buf, 0, (int)M_COPYALL);
160 	if (m == 0) {
161 		printf("hdlc: out of mbufs\n");
162 		return;
163 	}
164 	(*hdp->hd_output)(hdp, m);
165 	SET_TIMER (hdp);
166 }
167 
168 hd_ifoutput(hdp, m)
169 register struct mbuf *m;
170 register struct hdcb *hdp;
171 {
172 	/*
173 	 * Queue message on interface, and start output if interface
174 	 * not yet active.
175 	 */
176 	register struct ifnet *ifp = hdp->hd_ifp;
177 	int s = splimp();
178 
179 	if (IF_QFULL(&ifp->if_snd)) {
180 		IF_DROP(&ifp->if_snd);
181 	    /* printf("%s%d: HDLC says OK to send but queue full, may hang\n",
182 			ifp->if_name, ifp->if_unit);*/
183 		m_freem(m);
184 	} else {
185 		IF_ENQUEUE(&ifp->if_snd, m);
186 		if ((ifp->if_flags & IFF_OACTIVE) == 0)
187 			(*ifp->if_start)(ifp);
188 	}
189 	splx(s);
190 }
191 
192 
193 /*
194  *  This routine gets control when the timer expires because we have not
195  *  received an acknowledgement for a iframe.
196  */
197 
198 hd_resend_iframe (hdp)
199 register struct hdcb *hdp;
200 {
201 
202 	if (hdp->hd_retxcnt++ < hd_n2) {
203 		if (!(hdp->hd_condition & TIMER_RECOVERY_CONDITION)) {
204 			hdp->hd_xx = hdp->hd_vs;
205 			hdp->hd_condition |= TIMER_RECOVERY_CONDITION;
206 		}
207 
208 		hdp->hd_vs = hdp->hd_lastrxnr;
209 		hd_send_iframe (hdp, hdp->hd_retxq[hdp->hd_vs], POLLON);
210 	} else {
211 		/* At this point we have not received a RR even after N2
212 		   retries - attempt to reset link. */
213 
214 		hd_initvars (hdp);
215 		hd_writeinternal (hdp, SABM, POLLOFF);
216 		hdp->hd_state = WAIT_UA;
217 		SET_TIMER (hdp);
218 		hd_message (hdp, "Timer recovery failed: link down");
219 		(void) pk_ctlinput (PRC_LINKDOWN, hdp->hd_pkp);
220 	}
221 }
222