1 /*
2 *
3 * ===================================
4 * HARP | Host ATM Research Platform
5 * ===================================
6 *
7 *
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.
12 *
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.
20 *
21 * Copyright 1994-1998 Network Computing Services, Inc.
22 *
23 * Copies of this Software may be made, however, the above copyright
24 * notice must be reproduced on all copies.
25 *
26 * @(#) $FreeBSD: releng/5.3/sys/dev/hfa/fore_output.c 126122 2004-02-22 16:27:28Z mux $
27 *
28 */
29
30 /*
31 * FORE Systems 200-Series Adapter Support
32 * ---------------------------------------
33 *
34 * PDU output processing
35 *
36 */
37
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/types.h>
41 #include <sys/time.h>
42 #include <sys/socket.h>
43 #include <sys/socketvar.h>
44 #include <vm/vm.h>
45 #include <vm/pmap.h>
46 #include <net/if.h>
47 #include <netatm/port.h>
48 #include <netatm/queue.h>
49 #include <netatm/atm.h>
50 #include <netatm/atm_sys.h>
51 #include <netatm/atm_sap.h>
52 #include <netatm/atm_cm.h>
53 #include <netatm/atm_if.h>
54 #include <netatm/atm_vc.h>
55 #include <netatm/atm_stack.h>
56 #include <netatm/atm_pcb.h>
57 #include <netatm/atm_var.h>
58 #include <dev/pci/pcivar.h>
59 #include <dev/hfa/fore.h>
60 #include <dev/hfa/fore_aali.h>
61 #include <dev/hfa/fore_slave.h>
62 #include <dev/hfa/fore_stats.h>
63 #include <dev/hfa/fore_var.h>
64 #include <dev/hfa/fore_include.h>
65
66 #ifndef lint
67 __RCSID("@(#) $FreeBSD: releng/5.3/sys/dev/hfa/fore_output.c 126122 2004-02-22 16:27:28Z mux $");
68 #endif
69
70
71 /*
72 * Local functions
73 */
74 static KBuffer * fore_xmit_segment(Fore_unit *, KBuffer *,
75 H_xmit_queue *, int *, int *);
76 static void fore_seg_dma_free(H_xmit_queue *, KBuffer *, int);
77
78
79 /*
80 * Output a PDU
81 *
82 * This function is called via the common driver code after receiving a
83 * stack *_DATA* command. The common code has already validated most of
84 * the request so we just need to check a few more Fore-specific details.
85 * Then we just build a transmit descriptor request for the PDU and issue
86 * the command to the CP.
87 *
88 * Arguments:
89 * cup pointer to device common unit
90 * cvp pointer to common VCC entry
91 * m pointer to output PDU buffer chain head
92 *
93 * Returns:
94 * none
95 *
96 */
97 void
98 fore_output(cup, cvp, m)
99 Cmn_unit *cup;
100 Cmn_vcc *cvp;
101 KBuffer *m;
102 {
103 Fore_unit *fup = (Fore_unit *)cup;
104 Fore_vcc *fvp = (Fore_vcc *)cvp;
105 struct vccb *vcp;
106 H_xmit_queue *hxp;
107 Xmit_queue *cqp;
108 Xmit_descr *xdp;
109 int retry, nsegs, pdulen;
110 int s;
111
112 #ifdef DIAGNOSTIC
113 if (atm_dev_print)
114 atm_dev_pdu_print(cup, cvp, m, "fore_output");
115 #endif
116
117 vcp = fvp->fv_connvc->cvc_vcc;
118
119 /*
120 * If we're still waiting for activation to finish, delay for
121 * a little while before we toss the PDU
122 */
123 if (fvp->fv_state == CVS_INITED) {
124 retry = 3;
125 while (retry-- && (fvp->fv_state == CVS_INITED))
126 DELAY(1000);
127 if (fvp->fv_state != CVS_ACTIVE) {
128 /*
129 * Activation still hasn't finished, oh well....
130 */
131 fup->fu_stats->st_drv.drv_xm_notact++;
132 vcp->vc_oerrors++;
133 if (vcp->vc_nif)
134 vcp->vc_nif->nif_if.if_oerrors++;
135 KB_FREEALL(m);
136 return;
137 }
138 }
139
140 /*
141 * Queue PDU at end of transmit queue
142 *
143 * If queue is full we'll delay a bit before tossing the PDU
144 */
145 s = splnet();
146 hxp = fup->fu_xmit_tail;
147 if (!((*hxp->hxq_status) & QSTAT_FREE)) {
148
149 fup->fu_stats->st_drv.drv_xm_full++;
150 retry = 3;
151 do {
152 DELAY(1000);
153
154 DEVICE_LOCK((Cmn_unit *)fup);
155 fore_xmit_drain(fup);
156 DEVICE_UNLOCK((Cmn_unit *)fup);
157
158 } while (--retry && (!((*hxp->hxq_status) & QSTAT_FREE)));
159
160 if (!((*hxp->hxq_status) & QSTAT_FREE)) {
161 /*
162 * Queue is still full, bye-bye PDU
163 */
164 fup->fu_pif.pif_oerrors++;
165 vcp->vc_oerrors++;
166 if (vcp->vc_nif)
167 vcp->vc_nif->nif_if.if_oerrors++;
168 KB_FREEALL(m);
169 (void) splx(s);
170 return;
171 }
172 }
173
174 /*
175 * We've got a free transmit queue entry
176 */
177
178 /*
179 * Now build the transmit segment descriptors for this PDU
180 */
181 m = fore_xmit_segment(fup, m, hxp, &nsegs, &pdulen);
182 if (m == NULL) {
183 /*
184 * The build failed, buffer chain has been freed
185 */
186 vcp->vc_oerrors++;
187 if (vcp->vc_nif)
188 vcp->vc_nif->nif_if.if_oerrors++;
189 (void) splx(s);
190 return;
191 }
192
193 /*
194 * Set up the descriptor header
195 */
196 xdp = hxp->hxq_descr;
197 xdp->xd_cell_hdr = ATM_HDR_SET(vcp->vc_vpi, vcp->vc_vci, 0, 0);
198 xdp->xd_spec = XDS_SET_SPEC(0, fvp->fv_aal, nsegs, pdulen);
199 xdp->xd_rate = fvp->rate;
200
201 /*
202 * Everything is ready to go, so officially claim the host queue
203 * entry and setup the CP-resident queue entry. The CP will grab
204 * the PDU when the descriptor pointer is set.
205 */
206 fup->fu_xmit_tail = hxp->hxq_next;
207 hxp->hxq_buf = m;
208 hxp->hxq_vcc = fvp;
209 (*hxp->hxq_status) = QSTAT_PENDING;
210 cqp = hxp->hxq_cpelem;
211 cqp->cq_descr = (CP_dma)
212 CP_WRITE((u_long)hxp->hxq_descr_dma | XMIT_SEGS_TO_BLKS(nsegs));
213
214 (void) splx(s);
215
216 /*
217 * See if there are any completed queue entries
218 */
219 DEVICE_LOCK((Cmn_unit *)fup);
220 fore_xmit_drain(fup);
221 DEVICE_UNLOCK((Cmn_unit *)fup);
222
223 return;
224 }
225
226
227 /*
228 * Build Transmit Segment Descriptors
229 *
230 * This function will take a supplied buffer chain of data to be transmitted
231 * and build the transmit segment descriptors for the data. This will include
232 * the dreaded operation of ensuring that the data for each transmit segment
233 * is full-word aligned and (except for the last segment) is an integral number
234 * of words in length. If the data isn't already aligned and sized as
235 * required, then the data must be shifted (copied) into place - a sure
236 * performance killer. Note that we rely on the fact that all buffer data
237 * areas are allocated with (at least) full-word alignments/lengths.
238 *
239 * If any errors are encountered, the buffer chain will be freed.
240 *
241 * Arguments:
242 * fup pointer to device unit
243 * m pointer to output PDU buffer chain head
244 * hxp pointer to host transmit queue entry
245 * segp pointer to return the number of transmit segments
246 * lenp pointer to return the pdu length
247 *
248 * Returns:
249 * m build successful, pointer to (possibly new) head of
250 * output PDU buffer chain
251 * NULL build failed, buffer chain freed
252 *
253 */
254 static KBuffer *
255 fore_xmit_segment(fup, m, hxp, segp, lenp)
256 Fore_unit *fup;
257 KBuffer *m;
258 H_xmit_queue *hxp;
259 int *segp;
260 int *lenp;
261 {
262 Xmit_descr *xdp = hxp->hxq_descr;
263 Xmit_seg_descr *xsp;
264 H_dma *sdmap;
265 KBuffer *m0, *m1, *mprev;
266 caddr_t cp, bfr;
267 vm_paddr_t dma;
268 int pdulen, nsegs, len, align;
269 int compressed = 0;
270
271 m0 = m;
272
273 retry:
274 xsp = xdp->xd_seg;
275 sdmap = hxp->hxq_dma;
276 mprev = NULL;
277 pdulen = 0;
278 nsegs = 0;
279
280 /*
281 * Loop thru each buffer in the chain, performing the necessary
282 * data positioning and then building a segment descriptor for
283 * that data.
284 */
285 while (m) {
286 /*
287 * Get rid of any zero-length buffers
288 */
289 if (KB_LEN(m) == 0) {
290 if (mprev) {
291 KB_UNLINK(m, mprev, m1);
292 } else {
293 KB_UNLINKHEAD(m, m1);
294 m0 = m1;
295 }
296 m = m1;
297 continue;
298 }
299
300 /*
301 * Make sure we don't try to use too many segments
302 */
303 if (nsegs >= XMIT_MAX_SEGS) {
304 /*
305 * First, free already allocated DMA addresses
306 */
307 fore_seg_dma_free(hxp, m0, nsegs);
308
309 /*
310 * Try to compress buffer chain (but only once)
311 */
312 if (compressed) {
313 KB_FREEALL(m0);
314 return (NULL);
315 }
316
317 fup->fu_stats->st_drv.drv_xm_maxpdu++;
318
319 m = atm_dev_compress(m0);
320 if (m == NULL) {
321 return (NULL);
322 }
323
324 /*
325 * Build segment descriptors for compressed chain
326 */
327 m0 = m;
328 compressed = 1;
329 goto retry;
330 }
331
332 /*
333 * Get start of data onto full-word alignment
334 */
335 KB_DATASTART(m, cp, caddr_t);
336 if ((align = ((uintptr_t)cp) & (XMIT_SEG_ALIGN - 1)) != 0) {
337 /*
338 * Gotta slide the data up
339 */
340 fup->fu_stats->st_drv.drv_xm_segnoal++;
341 bfr = cp - align;
342 bcopy(cp, bfr, KB_LEN(m));
343 KB_HEADMOVE(m, -align);
344 } else {
345 /*
346 * Data already aligned
347 */
348 bfr = cp;
349 }
350
351 /*
352 * Now work on getting the data length correct
353 */
354 len = KB_LEN(m);
355 while ((align = (len & (XMIT_SEG_ALIGN - 1))) &&
356 (m1 = KB_NEXT(m))) {
357
358 /*
359 * Have to move some data from following buffer(s)
360 * to word-fill this buffer
361 */
362 int ncopy = MIN(XMIT_SEG_ALIGN - align, KB_LEN(m1));
363
364 if (ncopy) {
365 /*
366 * Move data to current buffer
367 */
368 caddr_t dest;
369
370 fup->fu_stats->st_drv.drv_xm_seglen++;
371 KB_DATASTART(m1, cp, caddr_t);
372 dest = bfr + len;
373 KB_HEADADJ(m1, -ncopy);
374 KB_TAILADJ(m, ncopy);
375 len += ncopy;
376 while (ncopy--) {
377 *dest++ = *cp++;
378 }
379 }
380
381 /*
382 * If we've drained the buffer, free it
383 */
384 if (KB_LEN(m1) == 0) {
385 KBuffer *m2;
386
387 KB_UNLINK(m1, m, m2);
388 }
389 }
390
391 /*
392 * Finally, build the segment descriptor
393 */
394
395 /*
396 * Round last segment to fullword length (if needed)
397 */
398 if (len & (XMIT_SEG_ALIGN - 1))
399 xsp->xsd_len = KB_LEN(m) =
400 (len + XMIT_SEG_ALIGN) & ~(XMIT_SEG_ALIGN - 1);
401 else
402 xsp->xsd_len = KB_LEN(m) = len;
403
404 /*
405 * Get a DMA address for the data
406 */
407 dma = vtophys(bfr);
408 if (dma == 0) {
409 fup->fu_stats->st_drv.drv_xm_segdma++;
410 fore_seg_dma_free(hxp, m0, nsegs);
411 KB_FREEALL(m0);
412 return (NULL);
413 }
414
415 /*
416 * Now we're really ready to call it a segment
417 */
418 *sdmap++ = xsp->xsd_buffer = (H_dma) dma;
419
420 /*
421 * Bump counters and get ready for next buffer
422 */
423 pdulen += len;
424 nsegs++;
425 xsp++;
426 mprev = m;
427 m = KB_NEXT(m);
428 }
429
430 /*
431 * Validate PDU length
432 */
433 if (pdulen > XMIT_MAX_PDULEN) {
434 fup->fu_stats->st_drv.drv_xm_maxpdu++;
435 fore_seg_dma_free(hxp, m0, nsegs);
436 KB_FREEALL(m0);
437 return (NULL);
438 }
439
440 /*
441 * Return the good news to the caller
442 */
443 *segp = nsegs;
444 *lenp = pdulen;
445
446 return (m0);
447 }
448
449
450 /*
451 * Free Transmit Segment Queue DMA addresses
452 *
453 * Arguments:
454 * hxp pointer to host transmit queue entry
455 * m0 pointer to output PDU buffer chain head
456 * nsegs number of processed transmit segments
457 *
458 * Returns:
459 * none
460 *
461 */
462 static void
463 fore_seg_dma_free(hxp, m0, nsegs)
464 H_xmit_queue *hxp;
465 KBuffer *m0;
466 int nsegs;
467 {
468 KBuffer *m = m0;
469 H_dma *sdmap = hxp->hxq_dma;
470 caddr_t cp;
471 int i;
472
473 for (i = 0; i < nsegs; i++) {
474 KB_DATASTART(m, cp, caddr_t);
475 m = KB_NEXT(m);
476 sdmap++;
477 }
478 }
479
Cache object: 88a02edd78bf24bab207771026f854c6
|