Commit | Line | Data |
---|---|---|
c91dccbd KS |
1 | /* |
2 | * Copyright (C) Dirk Husemann, Computer Science Department IV, | |
3 | * University of Erlangen-Nuremberg, Germany, 1990, 1991, 1992 | |
e7a3707f KB |
4 | * Copyright (c) 1992, 1993 |
5 | * The Regents of the University of California. All rights reserved. | |
c91dccbd KS |
6 | * |
7 | * This code is derived from software contributed to Berkeley by | |
8 | * Dirk Husemann and the Computer Science Department (IV) of | |
9 | * the University of Erlangen-Nuremberg, Germany. | |
10 | * | |
11 | * %sccs.include.redist.c% | |
12 | * | |
e7a3707f | 13 | * @(#)llc_output.c 8.1 (Berkeley) %G% |
c91dccbd KS |
14 | */ |
15 | ||
16 | #include <sys/param.h> | |
17 | #include <sys/systm.h> | |
18 | #include <sys/mbuf.h> | |
19 | #include <sys/domain.h> | |
20 | #include <sys/socket.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 | #include <net/if_dl.h> | |
28 | #include <net/if_llc.h> | |
29 | #include <net/route.h> | |
30 | ||
31 | #include <netccitt/dll.h> | |
32 | #include <netccitt/llc_var.h> | |
33 | ||
34 | /* | |
35 | * llc_output() --- called by an upper layer (network layer) entity whenever | |
36 | * there is an INFO frame to be transmitted. We enqueue the | |
37 | * info frame and call llc_start() to do the actual sending. | |
38 | */ | |
39 | ||
40 | llc_output(struct llc_linkcb *linkp, struct mbuf *m) | |
41 | { | |
42 | register int i; | |
43 | ||
44 | i = splimp(); | |
45 | LLC_ENQUEUE(linkp, m); | |
46 | llc_start(linkp); | |
47 | splx(i); | |
48 | ||
49 | } | |
50 | ||
51 | ||
52 | /* | |
53 | * llc_start() --- We try to subsequently dequeue all the frames available and | |
54 | * send them out. | |
55 | */ | |
56 | void | |
57 | llc_start(struct llc_linkcb *linkp) | |
58 | { | |
59 | register int i; | |
60 | register struct mbuf *m; | |
61 | int action; | |
62 | ||
63 | while ((LLC_STATEEQ(linkp, NORMAL) || LLC_STATEEQ(linkp, BUSY) || | |
64 | LLC_STATEEQ(linkp, REJECT)) && | |
65 | (linkp->llcl_slotsfree > 0) && | |
66 | (LLC_GETFLAG(linkp, REMOTE_BUSY) == 0)) { | |
67 | LLC_DEQUEUE(linkp, m); | |
68 | if (m == NULL) | |
69 | break; | |
70 | LLC_SETFRAME(linkp, m); | |
71 | (void)llc_statehandler(linkp, (struct llc *) 0, NL_DATA_REQUEST, | |
72 | 0, 0); | |
73 | } | |
74 | } | |
75 | ||
76 | ||
77 | /* | |
78 | * llc_send() --- Handles single frames. If dealing with INFO frames we need to | |
79 | * prepend the LLC header, otherwise we just allocate an mbuf. | |
80 | * In both cases the actual send is done by llc_rawsend(). | |
81 | */ | |
82 | llc_send(struct llc_linkcb *linkp, int frame_kind, int cmdrsp, int pollfinal) | |
83 | { | |
84 | register struct mbuf *m = (struct mbuf *)0; | |
85 | register struct llc *frame; | |
86 | ||
87 | if (frame_kind == LLCFT_INFO) | |
88 | m = linkp->llcl_output_buffers[llc_seq2slot(linkp, | |
89 | linkp->llcl_vs)]; | |
90 | LLC_GETHDR(frame, m); | |
91 | ||
92 | /* pass it on to llc_rawsend() */ | |
93 | llc_rawsend(linkp, m, frame, frame_kind, linkp->llcl_vs, cmdrsp, pollfinal); | |
94 | ||
95 | if (frame_kind == LLCFT_INFO) | |
96 | LLC_INC(linkp->llcl_vs); | |
97 | ||
98 | return 0; | |
99 | } | |
100 | ||
101 | /* | |
102 | * llc_resend() --- llc_resend() retransmits all unacknowledged INFO frames. | |
103 | */ | |
104 | llc_resend(struct llc_linkcb *linkp, int cmdrsp, int pollfinal) | |
105 | { | |
106 | register struct llc *frame; | |
107 | register struct mbuf *m; | |
108 | register int seq, slot; | |
109 | ||
110 | if (linkp->llcl_slotsfree < linkp->llcl_window) | |
111 | /* assert lock between nr_received & V(S) */ | |
112 | if (linkp->llcl_nr_received != linkp->llcl_vs) | |
113 | panic("llc: V(S) != N(R) received\n"); | |
114 | ||
115 | for (slot = llc_seq2slot(linkp, linkp->llcl_vs); | |
116 | slot != linkp->llcl_freeslot; | |
117 | LLC_INC(linkp->llcl_vs), | |
118 | slot = llc_seq2slot(linkp, linkp->llcl_vs)) { | |
119 | m = linkp->llcl_output_buffers[slot]; | |
120 | LLC_GETHDR(frame, m); | |
121 | llc_rawsend(linkp, m, frame, LLCFT_INFO, linkp->llcl_vs, | |
122 | cmdrsp, pollfinal); | |
123 | pollfinal = 0; | |
124 | } | |
125 | ||
126 | return 0; | |
127 | } | |
128 | ||
129 | /* | |
130 | * llc_rawsend() --- constructs an LLC frame and sends it out via the | |
131 | * associated interface of the link control block. | |
132 | * | |
133 | * We need to make sure that outgoing frames have the correct length, | |
134 | * in particular the 4 byte ones (RR, RNR, REJ) as LLC_GETHDR() will | |
135 | * set the mbuf len to 3 as default len for non INFO frames ... | |
136 | * | |
137 | * Frame kind Length (w/o MAC header, {D,S}SAP incl.) | |
138 | * -------------------------------------------------------------- | |
139 | * DISC, SABME, UA, DM 3 bytes ({D,S}SAP + CONTROL) | |
140 | * RR, RNR, REJ 4 bytes ({D,S}SAP + CONTROL0 + CONTROL1) | |
141 | * XID 6 bytes ({D,S}SAP + CONTROL0 + FI,CLASS,WINDOW) | |
142 | * FRMR 7 bytes ({D,S}SAP + CONTROL0 + REJ CONTROL,V(S),V(R),CAUSE) | |
143 | * INFO 4 -- MTU | |
144 | * UI, TEST 3 -- MTU | |
145 | * | |
146 | */ | |
147 | #define LLC_SETLEN(m, l) (m)->m_pkthdr.len = (m)->m_len = (l) | |
148 | ||
149 | llc_rawsend(struct llc_linkcb *linkp, struct mbuf *m, struct llc *frame, | |
150 | int frame_kind, int vs, int cmdrsp, int pollfinal) | |
151 | { | |
152 | register short adjust = LLC_UFRAMELEN; | |
153 | struct ifnet *ifp; | |
154 | ||
155 | switch (frame_kind) { | |
156 | /* supervisory and information frames */ | |
157 | case LLCFT_INFO: | |
158 | frame->llc_control = LLC_INFO; | |
159 | LLCSBITS(frame->llc_control, i_ns, vs); | |
160 | LLCSBITS(frame->llc_control_ext, i_nr, linkp->llcl_vr); | |
161 | adjust = LLC_ISFRAMELEN; | |
162 | break; | |
163 | case LLCFT_RR: | |
164 | frame->llc_control = LLC_RR; | |
165 | LLC_SETLEN(m, LLC_ISFRAMELEN); | |
166 | LLCSBITS(frame->llc_control_ext, s_nr, linkp->llcl_vr); | |
167 | adjust = LLC_ISFRAMELEN; | |
168 | break; | |
169 | case LLCFT_RNR: | |
170 | frame->llc_control = LLC_RNR; | |
171 | LLC_SETLEN(m, LLC_ISFRAMELEN); | |
172 | LLCSBITS(frame->llc_control_ext, s_nr, linkp->llcl_vr); | |
173 | adjust = LLC_ISFRAMELEN; | |
174 | break; | |
175 | case LLCFT_REJ: | |
176 | frame->llc_control = LLC_REJ; | |
177 | LLC_SETLEN(m, LLC_ISFRAMELEN); | |
178 | LLCSBITS(frame->llc_control_ext, s_nr, linkp->llcl_vr); | |
179 | adjust = LLC_ISFRAMELEN; | |
180 | break; | |
181 | /* unnumbered frames */ | |
182 | case LLCFT_DM: | |
183 | frame->llc_control = LLC_DM; | |
184 | break; | |
185 | case LLCFT_SABME: | |
186 | frame->llc_control = LLC_SABME; | |
187 | break; | |
188 | case LLCFT_DISC: | |
189 | frame->llc_control = LLC_DISC; | |
190 | break; | |
191 | case LLCFT_UA: | |
192 | frame->llc_control = LLC_UA; | |
193 | break; | |
194 | case LLCFT_UI: | |
195 | frame->llc_control = LLC_UI; | |
196 | break; | |
197 | case LLCFT_FRMR: | |
198 | frame->llc_control = LLC_FRMR; | |
199 | /* get more space --- FRMR frame are longer then usual */ | |
200 | LLC_SETLEN(m, LLC_FRMRLEN); | |
201 | bcopy((caddr_t) &linkp->llcl_frmrinfo, | |
202 | (caddr_t) &frame->llc_frmrinfo, | |
203 | sizeof(struct frmrinfo)); | |
204 | break; | |
205 | default: | |
206 | /* | |
207 | * We don't send {XID, TEST} frames | |
208 | */ | |
209 | if (m) | |
210 | m_freem(m); | |
211 | return; | |
212 | } | |
213 | ||
214 | /* | |
215 | * Fill in DSAP/SSAP | |
216 | */ | |
217 | frame->llc_dsap = frame->llc_ssap = LLSAPADDR(&linkp->llcl_addr); | |
218 | frame->llc_ssap |= cmdrsp; | |
219 | ||
220 | /* | |
221 | * Check for delayed action pending. ISO 8802-2, 7.9.2 (5) | |
222 | * and ISO 8802-2, 7.9.2.3 (32), (34), (36) pertain to this | |
223 | * piece of code --- hopefully we got it right here (i.e. | |
224 | * in the spirit of (32), (34), and (36) ... | |
225 | */ | |
226 | switch (frame_kind) { | |
227 | case LLCFT_RR: | |
228 | case LLCFT_RNR: | |
229 | case LLCFT_REJ: | |
230 | case LLCFT_INFO: | |
231 | switch (LLC_GETFLAG(linkp, DACTION)) { | |
232 | case LLC_DACKCMD: | |
233 | case LLC_DACKRSP: | |
234 | LLC_STOPTIMER(linkp, DACTION); | |
235 | break; | |
236 | case LLC_DACKCMDPOLL: | |
237 | if (cmdrsp == LLC_CMD) { | |
238 | pollfinal = 1; | |
239 | LLC_STOPTIMER(linkp, DACTION); | |
240 | } | |
241 | break; | |
242 | case LLC_DACKRSPFINAL: | |
243 | if (cmdrsp == LLC_RSP) { | |
244 | pollfinal = 1; | |
245 | LLC_STOPTIMER(linkp, DACTION); | |
246 | } | |
247 | break; | |
248 | } | |
249 | break; | |
250 | } | |
251 | ||
252 | if (adjust == LLC_UFRAMELEN) | |
253 | LLCSBITS(frame->llc_control, u_pf, pollfinal); | |
254 | else LLCSBITS(frame->llc_control_ext, s_pf, pollfinal); | |
255 | ||
256 | /* | |
257 | * Get interface to send frame onto | |
258 | */ | |
259 | ifp = linkp->llcl_if; | |
260 | if (frame_kind == LLCFT_INFO) { | |
261 | /* | |
262 | * send out a copy of the frame, retain the | |
263 | * original | |
264 | */ | |
265 | (*ifp->if_output)(ifp, m_copy(m, 0, (int)M_COPYALL), | |
266 | rt_key(linkp->llcl_nlrt), | |
267 | linkp->llcl_nlrt); | |
268 | /* | |
269 | * Account for the LLC header and let it ``disappear'' | |
270 | * as the raw info frame payload is what we hold in | |
271 | * the output_buffers of the link. | |
272 | */ | |
273 | m_adj(m, LLC_ISFRAMELEN); | |
274 | } else (*ifp->if_output)(ifp, m, | |
275 | rt_key(linkp->llcl_nlrt), | |
276 | linkp->llcl_nlrt); | |
277 | } | |
278 |