xref: /original-bsd/sys/netccitt/hd_output.c (revision abd50c55)
1 /* Copyright (c) University of British Columbia, 1984 */
2 
3 #include "../h/param.h"
4 #include "../h/systm.h"
5 #include "../h/mbuf.h"
6 #include "../h/domain.h"
7 #include "../h/socket.h"
8 #include "../h/protosw.h"
9 #include "../h/errno.h"
10 #include "../h/time.h"
11 #include "../h/kernel.h"
12 #include "../net/if.h"
13 
14 #include "../netccitt/hdlc.h"
15 #include "../netccitt/hd_var.h"
16 #include "../netccitt/x25.h"
17 
18 /*
19  *      HDLC OUTPUT INTERFACE
20  *
21  *      This routine is called when the X.25 packet layer output routine
22  *      has a information frame (iframe)  to write.   It is  also called
23  *      by the input and control routines of the HDLC layer.
24  */
25 
26 hd_output (m, xcp)
27 struct x25config *xcp;
28 register struct mbuf *m;
29 {
30 	register struct hdcb *hdp;
31 	static struct x25config *lastxcp;
32 	static struct hdcb *lasthdp;
33 
34 	if (m == NULL)
35 		panic ("hd_output");
36 
37 	if (xcp == lastxcp)
38 		hdp = lasthdp;
39 	else {
40 		for (hdp = hdcbhead; ; hdp = hdp->hd_next) {
41 			if (hdp == 0) {
42 				printf("hd_output: can't find hdcb for %X\n", xcp);
43 				m_freem (m);
44 				return;
45 			}
46 			if (hdp->hd_xcp == xcp)
47 				break;
48 		}
49 		lastxcp = xcp;
50 		lasthdp = hdp;
51 	}
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 	if (m->m_off < MMINOFF + HDHEADERLN) {
65 		register struct mbuf *m0;
66 
67 		m0 = m_get (M_DONTWAIT, MT_DATA);
68 		if (m0 == NULL) {
69 			m_freem (m);
70 			return;
71 		}
72 		m0->m_next = m;
73 		m0->m_len = HDHEADERLN;
74 		m = m0;
75 	} else {
76 		m->m_off -= HDHEADERLN;
77 		m->m_len += HDHEADERLN;
78 	}
79 
80 	hd_append (&hdp->hd_txq, m);
81 	hd_start (hdp);
82 }
83 
84 hd_start (hdp)
85 register struct hdcb *hdp;
86 {
87 	register struct mbuf *m;
88 
89 	/*
90 	 * The iframe is only transmitted if all these conditions are FALSE.
91 	 * The iframe remains queued (hdp->hd_txq) however and will be
92 	 * transmitted as soon as these conditions are cleared.
93 	 */
94 
95 	while (!(hdp->hd_condition & (TIMER_RECOVERY_CONDITION | REMOTE_RNR_CONDITION | REJ_CONDITION))) {
96 		if (hdp->hd_vs == (hdp->hd_lastrxnr + hdp->hd_xcp->xc_lwsize) % MODULUS) {
97 
98 			/* We have now exceeded the  maximum  number  of
99 			   outstanding iframes. Therefore,  we must wait
100 			   until  at least  one is acknowledged if this
101 			   condition  is not  turned off before we are
102 			   requested to write another iframe. */
103 			hdp->hd_window_condition++;
104 			break;
105 		}
106 
107 		/* hd_remove top iframe from transmit queue. */
108 		if ((m = hd_remove (&hdp->hd_txq)) == NULL)
109 			break;
110 
111 		hd_send_iframe (hdp, m, POLLOFF);
112 	}
113 }
114 
115 /*
116  *  This procedure is passed a buffer descriptor for an iframe. It builds
117  *  the rest of the control part of the frame and then writes it out.  It
118  *  also  starts the  acknowledgement  timer and keeps  the iframe in the
119  *  Retransmit queue (Retxq) just in case  we have to do this again.
120  *
121  *  Note: This routine is also called from hd_input.c when retransmission
122  *       of old frames is required.
123  */
124 
125 hd_send_iframe (hdp, buf, poll_bit)
126 register struct hdcb *hdp;
127 register struct mbuf *buf;
128 int poll_bit;
129 {
130 	register struct Hdlc_iframe *iframe;
131 	register struct ifnet *ifp = hdp->hd_ifp;
132 	struct mbuf *m;
133 
134 	KILL_TIMER (hdp);
135 
136 	if (buf == 0) {
137 		printf ("hd_send_iframe: zero arg\n");
138 #ifdef HDLCDEBUG
139 		hd_status (hdp);
140 		hd_dumptrace (hdp);
141 #endif
142 		hdp->hd_vs = (hdp->hd_vs + 7) % MODULUS;
143 		return;
144 	}
145 	iframe = mtod (buf, struct Hdlc_iframe *);
146 
147 	iframe -> hdlc_0 = 0;
148 	iframe -> nr = hdp->hd_vr;
149 	iframe -> pf = poll_bit;
150 	iframe -> ns = hdp->hd_vs;
151 	iframe -> address = ADDRESS_B;
152 	hdp->hd_lasttxnr = hdp->hd_vr;
153 	hdp->hd_rrtimer = 0;
154 
155 	if (hdp->hd_vs == hdp->hd_retxqi) {
156 		/* Check for retransmissions. */
157 		/* Put iframe only once in the Retransmission queue. */
158 		hdp->hd_retxq[hdp->hd_retxqi] = buf;
159 		hdp->hd_retxqi = (hdp->hd_retxqi + 1) % MODULUS;
160 		hdp->hd_iframes_out++;
161 	}
162 
163 	hdp->hd_vs = (hdp->hd_vs + 1) % MODULUS;
164 
165 	hd_trace (hdp, TX, (struct Hdlc_frame *)iframe);
166 
167 	/* Write buffer on device. */
168 	m = hdp->hd_dontcopy ? buf : m_copy(buf, 0, (int)M_COPYALL);
169 	if (m == 0) {
170 		printf("hdlc: out of mbufs\n");
171 		return;
172 	}
173 	(*ifp -> if_output) (ifp, m, (struct sockaddr *)hdp->hd_xcp);
174 
175 	SET_TIMER (hdp);
176 }
177 
178 /*
179  *  This routine gets control when the timer expires because we have not
180  *  received an acknowledgement for a iframe.
181  */
182 
183 hd_resend_iframe (hdp)
184 register struct hdcb *hdp;
185 {
186 
187 	if (hdp->hd_retxcnt++ < hd_n2) {
188 		if (!(hdp->hd_condition & TIMER_RECOVERY_CONDITION)) {
189 			hdp->hd_xx = hdp->hd_vs;
190 			hdp->hd_condition |= TIMER_RECOVERY_CONDITION;
191 		}
192 
193 		hdp->hd_vs = hdp->hd_lastrxnr;
194 		hd_send_iframe (hdp, hdp->hd_retxq[hdp->hd_vs], POLLON);
195 	} else {
196 		/* At this point we have not received a RR even after N2
197 		   retries - attempt to reset link. */
198 
199 		hd_initvars (hdp);
200 		hd_writeinternal (hdp, SABM, POLLOFF);
201 		hdp->hd_state = WAIT_UA;
202 		SET_TIMER (hdp);
203 		hd_message (hdp, "Timer recovery failed: link down");
204 		(void) pk_ctlinput (PRC_LINKDOWN, hdp->hd_xcp);
205 	}
206 }
207