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/dev/hfa/fore_output.c,v 1.5 2000/01/15 21:01:04 mks Exp $
27 * @(#) $DragonFly: src/sys/dev/atm/hfa/fore_output.c,v 1.6 2008/03/01 22:03:13 swildner Exp $
31 * FORE Systems 200-Series Adapter Support
32 * ---------------------------------------
34 * PDU output processing
38 #include "fore_include.h"
43 static KBuffer
* fore_xmit_segment (Fore_unit
*, KBuffer
*,
44 H_xmit_queue
*, int *, int *);
45 static void fore_seg_dma_free (H_xmit_queue
*, KBuffer
*, int);
51 * This function is called via the common driver code after receiving a
52 * stack *_DATA* command. The common code has already validated most of
53 * the request so we just need to check a few more Fore-specific details.
54 * Then we just build a transmit descriptor request for the PDU and issue
55 * the command to the CP.
58 * cup pointer to device common unit
59 * cvp pointer to common VCC entry
60 * m pointer to output PDU buffer chain head
67 fore_output(Cmn_unit
*cup
, Cmn_vcc
*cvp
, KBuffer
*m
)
69 Fore_unit
*fup
= (Fore_unit
*)cup
;
70 Fore_vcc
*fvp
= (Fore_vcc
*)cvp
;
75 int retry
, nsegs
, pdulen
;
79 atm_dev_pdu_print(cup
, cvp
, m
, "fore_output");
82 vcp
= fvp
->fv_connvc
->cvc_vcc
;
85 * If we're still waiting for activation to finish, delay for
86 * a little while before we toss the PDU
88 if (fvp
->fv_state
== CVS_INITED
) {
90 while (retry
-- && (fvp
->fv_state
== CVS_INITED
))
92 if (fvp
->fv_state
!= CVS_ACTIVE
) {
94 * Activation still hasn't finished, oh well....
96 fup
->fu_stats
->st_drv
.drv_xm_notact
++;
99 vcp
->vc_nif
->nif_if
.if_oerrors
++;
106 * Queue PDU at end of transmit queue
108 * If queue is full we'll delay a bit before tossing the PDU
111 hxp
= fup
->fu_xmit_tail
;
112 if (!((*hxp
->hxq_status
) & QSTAT_FREE
)) {
114 fup
->fu_stats
->st_drv
.drv_xm_full
++;
119 DEVICE_LOCK((Cmn_unit
*)fup
);
120 fore_xmit_drain(fup
);
121 DEVICE_UNLOCK((Cmn_unit
*)fup
);
123 } while (--retry
&& (!((*hxp
->hxq_status
) & QSTAT_FREE
)));
125 if (!((*hxp
->hxq_status
) & QSTAT_FREE
)) {
127 * Queue is still full, bye-bye PDU
129 fup
->fu_pif
.pif_oerrors
++;
132 vcp
->vc_nif
->nif_if
.if_oerrors
++;
140 * We've got a free transmit queue entry
144 * Now build the transmit segment descriptors for this PDU
146 m
= fore_xmit_segment(fup
, m
, hxp
, &nsegs
, &pdulen
);
149 * The build failed, buffer chain has been freed
153 vcp
->vc_nif
->nif_if
.if_oerrors
++;
159 * Set up the descriptor header
161 xdp
= hxp
->hxq_descr
;
162 xdp
->xd_cell_hdr
= ATM_HDR_SET(vcp
->vc_vpi
, vcp
->vc_vci
, 0, 0);
163 xdp
->xd_spec
= XDS_SET_SPEC(0, fvp
->fv_aal
, nsegs
, pdulen
);
164 xdp
->xd_rate
= FORE_DEF_RATE
;
167 * Everything is ready to go, so officially claim the host queue
168 * entry and setup the CP-resident queue entry. The CP will grab
169 * the PDU when the descriptor pointer is set.
171 fup
->fu_xmit_tail
= hxp
->hxq_next
;
174 (*hxp
->hxq_status
) = QSTAT_PENDING
;
175 cqp
= hxp
->hxq_cpelem
;
176 cqp
->cq_descr
= (CP_dma
)
177 CP_WRITE((u_long
)hxp
->hxq_descr_dma
| XMIT_SEGS_TO_BLKS(nsegs
));
182 * See if there are any completed queue entries
184 DEVICE_LOCK((Cmn_unit
*)fup
);
185 fore_xmit_drain(fup
);
186 DEVICE_UNLOCK((Cmn_unit
*)fup
);
191 * Build Transmit Segment Descriptors
193 * This function will take a supplied buffer chain of data to be transmitted
194 * and build the transmit segment descriptors for the data. This will include
195 * the dreaded operation of ensuring that the data for each transmit segment
196 * is full-word aligned and (except for the last segment) is an integral number
197 * of words in length. If the data isn't already aligned and sized as
198 * required, then the data must be shifted (copied) into place - a sure
199 * performance killer. Note that we rely on the fact that all buffer data
200 * areas are allocated with (at least) full-word alignments/lengths.
202 * If any errors are encountered, the buffer chain will be freed.
205 * fup pointer to device unit
206 * m pointer to output PDU buffer chain head
207 * hxp pointer to host transmit queue entry
208 * segp pointer to return the number of transmit segments
209 * lenp pointer to return the pdu length
212 * m build successful, pointer to (possibly new) head of
213 * output PDU buffer chain
214 * NULL build failed, buffer chain freed
218 fore_xmit_segment(Fore_unit
*fup
, KBuffer
*m
, H_xmit_queue
*hxp
, int *segp
,
221 Xmit_descr
*xdp
= hxp
->hxq_descr
;
224 KBuffer
*m0
, *m1
, *mprev
;
227 int pdulen
, nsegs
, len
, align
;
234 sdmap
= hxp
->hxq_dma
;
240 * Loop thru each buffer in the chain, performing the necessary
241 * data positioning and then building a segment descriptor for
246 * Get rid of any zero-length buffers
248 if (KB_LEN(m
) == 0) {
250 KB_UNLINK(m
, mprev
, m1
);
252 KB_UNLINKHEAD(m
, m1
);
260 * Make sure we don't try to use too many segments
262 if (nsegs
>= XMIT_MAX_SEGS
) {
264 * First, free already allocated DMA addresses
266 fore_seg_dma_free(hxp
, m0
, nsegs
);
269 * Try to compress buffer chain (but only once)
276 fup
->fu_stats
->st_drv
.drv_xm_maxpdu
++;
278 m
= atm_dev_compress(m0
);
284 * Build segment descriptors for compressed chain
292 * Get start of data onto full-word alignment
294 KB_DATASTART(m
, cp
, caddr_t
);
295 if ((align
= ((u_int
)cp
) & (XMIT_SEG_ALIGN
- 1)) != 0) {
297 * Gotta slide the data up
299 fup
->fu_stats
->st_drv
.drv_xm_segnoal
++;
301 KM_COPY(cp
, bfr
, KB_LEN(m
));
302 KB_HEADMOVE(m
, -align
);
305 * Data already aligned
311 * Now work on getting the data length correct
314 while ((align
= (len
& (XMIT_SEG_ALIGN
- 1))) &&
318 * Have to move some data from following buffer(s)
319 * to word-fill this buffer
321 int ncopy
= MIN(XMIT_SEG_ALIGN
- align
, KB_LEN(m1
));
325 * Move data to current buffer
329 fup
->fu_stats
->st_drv
.drv_xm_seglen
++;
330 KB_DATASTART(m1
, cp
, caddr_t
);
332 KB_HEADADJ(m1
, -ncopy
);
333 KB_TAILADJ(m
, ncopy
);
341 * If we've drained the buffer, free it
343 if (KB_LEN(m1
) == 0) {
346 KB_UNLINK(m1
, m
, m2
);
351 * Finally, build the segment descriptor
355 * Round last segment to fullword length (if needed)
357 if (len
& (XMIT_SEG_ALIGN
- 1))
358 xsp
->xsd_len
= KB_LEN(m
) =
359 (len
+ XMIT_SEG_ALIGN
) & ~(XMIT_SEG_ALIGN
- 1);
361 xsp
->xsd_len
= KB_LEN(m
) = len
;
364 * Get a DMA address for the data
366 dma
= DMA_GET_ADDR(bfr
, xsp
->xsd_len
, XMIT_SEG_ALIGN
, 0);
368 fup
->fu_stats
->st_drv
.drv_xm_segdma
++;
369 fore_seg_dma_free(hxp
, m0
, nsegs
);
375 * Now we're really ready to call it a segment
377 *sdmap
++ = xsp
->xsd_buffer
= (H_dma
) dma
;
380 * Bump counters and get ready for next buffer
390 * Validate PDU length
392 if (pdulen
> XMIT_MAX_PDULEN
) {
393 fup
->fu_stats
->st_drv
.drv_xm_maxpdu
++;
394 fore_seg_dma_free(hxp
, m0
, nsegs
);
400 * Return the good news to the caller
410 * Free Transmit Segment Queue DMA addresses
413 * hxp pointer to host transmit queue entry
414 * m0 pointer to output PDU buffer chain head
415 * nsegs number of processed transmit segments
422 fore_seg_dma_free(H_xmit_queue
*hxp
, KBuffer
*m0
, int nsegs
)
425 H_dma
*sdmap
= hxp
->hxq_dma
;
429 for (i
= 0; i
< nsegs
; i
++) {
430 KB_DATASTART(m
, cp
, caddr_t
);
431 DMA_FREE_ADDR(cp
, *sdmap
, KB_LEN(m
), 0);