Initial import of binutils 2.22 on the new vendor branch
[dragonfly.git] / sys / dev / atm / hfa / fore_output.c
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: 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 $
28  */
29
30 /*
31  * FORE Systems 200-Series Adapter Support
32  * ---------------------------------------
33  *
34  * PDU output processing
35  *
36  */
37
38 #include "fore_include.h"
39
40 /*
41  * Local functions
42  */
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);
46
47
48 /*
49  * Output a PDU
50  * 
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.  
56  *
57  * Arguments:
58  *      cup     pointer to device common unit
59  *      cvp     pointer to common VCC entry
60  *      m       pointer to output PDU buffer chain head
61  *
62  * Returns:
63  *      none
64  *
65  */
66 void
67 fore_output(Cmn_unit *cup, Cmn_vcc *cvp, KBuffer *m)
68 {
69         Fore_unit       *fup = (Fore_unit *)cup;
70         Fore_vcc        *fvp = (Fore_vcc *)cvp;
71         struct vccb     *vcp;
72         H_xmit_queue    *hxp;
73         Xmit_queue      *cqp;
74         Xmit_descr      *xdp;
75         int             retry, nsegs, pdulen;
76
77 #ifdef DIAGNOSTIC
78         if (atm_dev_print)
79                 atm_dev_pdu_print(cup, cvp, m, "fore_output");
80 #endif
81
82         vcp = fvp->fv_connvc->cvc_vcc;
83
84         /*
85          * If we're still waiting for activation to finish, delay for
86          * a little while before we toss the PDU
87          */
88         if (fvp->fv_state == CVS_INITED) {
89                 retry = 3;
90                 while (retry-- && (fvp->fv_state == CVS_INITED))
91                         DELAY(1000);
92                 if (fvp->fv_state != CVS_ACTIVE) {
93                         /*
94                          * Activation still hasn't finished, oh well....
95                          */
96                         fup->fu_stats->st_drv.drv_xm_notact++;
97                         vcp->vc_oerrors++;
98                         if (vcp->vc_nif)
99                                 vcp->vc_nif->nif_if.if_oerrors++;
100                         KB_FREEALL(m);
101                         return;
102                 }
103         }
104
105         /*
106          * Queue PDU at end of transmit queue
107          *
108          * If queue is full we'll delay a bit before tossing the PDU
109          */
110         crit_enter();
111         hxp = fup->fu_xmit_tail;
112         if (!((*hxp->hxq_status) & QSTAT_FREE)) {
113
114                 fup->fu_stats->st_drv.drv_xm_full++;
115                 retry = 3;
116                 do {
117                         DELAY(1000);
118
119                         DEVICE_LOCK((Cmn_unit *)fup);
120                         fore_xmit_drain(fup);
121                         DEVICE_UNLOCK((Cmn_unit *)fup);
122
123                 } while (--retry && (!((*hxp->hxq_status) & QSTAT_FREE)));
124
125                 if (!((*hxp->hxq_status) & QSTAT_FREE)) {
126                         /*
127                          * Queue is still full, bye-bye PDU
128                          */
129                         fup->fu_pif.pif_oerrors++;
130                         vcp->vc_oerrors++;
131                         if (vcp->vc_nif)
132                                 vcp->vc_nif->nif_if.if_oerrors++;
133                         KB_FREEALL(m);
134                         crit_exit();
135                         return;
136                 }
137         }
138
139         /*
140          * We've got a free transmit queue entry
141          */
142
143         /*
144          * Now build the transmit segment descriptors for this PDU
145          */
146         m = fore_xmit_segment(fup, m, hxp, &nsegs, &pdulen);
147         if (m == NULL) {
148                 /*
149                  * The build failed, buffer chain has been freed
150                  */
151                 vcp->vc_oerrors++;
152                 if (vcp->vc_nif)
153                         vcp->vc_nif->nif_if.if_oerrors++;
154                 crit_exit();
155                 return;
156         }
157
158         /*
159          * Set up the descriptor header
160          */
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;
165
166         /*
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.
170          */
171         fup->fu_xmit_tail = hxp->hxq_next;
172         hxp->hxq_buf = m;
173         hxp->hxq_vcc = fvp;
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));
178
179         crit_exit();
180
181         /*
182          * See if there are any completed queue entries
183          */
184         DEVICE_LOCK((Cmn_unit *)fup);
185         fore_xmit_drain(fup);
186         DEVICE_UNLOCK((Cmn_unit *)fup);
187 }
188
189
190 /*
191  * Build Transmit Segment Descriptors
192  * 
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.
201  *
202  * If any errors are encountered, the buffer chain will be freed.
203  * 
204  * Arguments:
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
210  *
211  * Returns:
212  *      m       build successful, pointer to (possibly new) head of 
213  *              output PDU buffer chain
214  *      NULL    build failed, buffer chain freed
215  *
216  */
217 static KBuffer *
218 fore_xmit_segment(Fore_unit *fup, KBuffer *m, H_xmit_queue *hxp, int *segp,
219                   int *lenp)
220 {
221         Xmit_descr      *xdp = hxp->hxq_descr;
222         Xmit_seg_descr  *xsp;
223         H_dma           *sdmap;
224         KBuffer         *m0, *m1, *mprev;
225         caddr_t         cp, bfr;
226         void            *dma;
227         int             pdulen, nsegs, len, align;
228         int             compressed = 0;
229
230         m0 = m;
231
232 retry:
233         xsp = xdp->xd_seg;
234         sdmap = hxp->hxq_dma;
235         mprev = NULL;
236         pdulen = 0;
237         nsegs = 0;
238
239         /*
240          * Loop thru each buffer in the chain, performing the necessary
241          * data positioning and then building a segment descriptor for
242          * that data.
243          */
244         while (m) {
245                 /*
246                  * Get rid of any zero-length buffers
247                  */
248                 if (KB_LEN(m) == 0) {
249                         if (mprev) {
250                                 KB_UNLINK(m, mprev, m1);
251                         } else {
252                                 KB_UNLINKHEAD(m, m1);
253                                 m0 = m1;
254                         }
255                         m = m1;
256                         continue;
257                 }
258
259                 /*
260                  * Make sure we don't try to use too many segments
261                  */
262                 if (nsegs >= XMIT_MAX_SEGS) {
263                         /*
264                          * First, free already allocated DMA addresses
265                          */
266                         fore_seg_dma_free(hxp, m0, nsegs);
267
268                         /*
269                          * Try to compress buffer chain (but only once)
270                          */
271                         if (compressed) {
272                                 KB_FREEALL(m0);
273                                 return (NULL);
274                         }
275
276                         fup->fu_stats->st_drv.drv_xm_maxpdu++;
277
278                         m = atm_dev_compress(m0);
279                         if (m == NULL) {
280                                 return (NULL);
281                         }
282
283                         /*
284                          * Build segment descriptors for compressed chain
285                          */
286                         m0 = m;
287                         compressed = 1;
288                         goto retry;
289                 }
290
291                 /*
292                  * Get start of data onto full-word alignment
293                  */
294                 KB_DATASTART(m, cp, caddr_t);
295                 if ((align = ((u_int)cp) & (XMIT_SEG_ALIGN - 1)) != 0) {
296                         /*
297                          * Gotta slide the data up
298                          */
299                         fup->fu_stats->st_drv.drv_xm_segnoal++;
300                         bfr = cp - align;
301                         KM_COPY(cp, bfr, KB_LEN(m));
302                         KB_HEADMOVE(m, -align);
303                 } else {
304                         /*
305                          * Data already aligned
306                          */
307                         bfr = cp;
308                 }
309
310                 /*
311                  * Now work on getting the data length correct
312                  */
313                 len = KB_LEN(m);
314                 while ((align = (len & (XMIT_SEG_ALIGN - 1))) &&
315                        (m1 = KB_NEXT(m))) {
316
317                         /*
318                          * Have to move some data from following buffer(s)
319                          * to word-fill this buffer
320                          */
321                         int     ncopy = MIN(XMIT_SEG_ALIGN - align, KB_LEN(m1));
322
323                         if (ncopy) {
324                                 /*
325                                  * Move data to current buffer
326                                  */
327                                 caddr_t         dest;
328
329                                 fup->fu_stats->st_drv.drv_xm_seglen++;
330                                 KB_DATASTART(m1, cp, caddr_t);
331                                 dest = bfr + len;
332                                 KB_HEADADJ(m1, -ncopy);
333                                 KB_TAILADJ(m, ncopy);
334                                 len += ncopy;
335                                 while (ncopy--) {
336                                         *dest++ = *cp++;
337                                 }
338                         }
339
340                         /*
341                          * If we've drained the buffer, free it
342                          */
343                         if (KB_LEN(m1) == 0) {
344                                 KBuffer         *m2;
345
346                                 KB_UNLINK(m1, m, m2);
347                         }
348                 }
349
350                 /*
351                  * Finally, build the segment descriptor
352                  */
353
354                 /*
355                  * Round last segment to fullword length (if needed)
356                  */
357                 if (len & (XMIT_SEG_ALIGN - 1))
358                         xsp->xsd_len = KB_LEN(m) =
359                                 (len + XMIT_SEG_ALIGN) & ~(XMIT_SEG_ALIGN - 1);
360                 else
361                         xsp->xsd_len = KB_LEN(m) = len;
362
363                 /*
364                  * Get a DMA address for the data
365                  */
366                 dma = DMA_GET_ADDR(bfr, xsp->xsd_len, XMIT_SEG_ALIGN, 0);
367                 if (dma == NULL) {
368                         fup->fu_stats->st_drv.drv_xm_segdma++;
369                         fore_seg_dma_free(hxp, m0, nsegs);
370                         KB_FREEALL(m0);
371                         return (NULL);
372                 }
373
374                 /*
375                  * Now we're really ready to call it a segment
376                  */
377                 *sdmap++ = xsp->xsd_buffer = (H_dma) dma;
378
379                 /*
380                  * Bump counters and get ready for next buffer
381                  */
382                 pdulen += len;
383                 nsegs++;
384                 xsp++;
385                 mprev = m;
386                 m = KB_NEXT(m);
387         }
388
389         /*
390          * Validate PDU length
391          */
392         if (pdulen > XMIT_MAX_PDULEN) {
393                 fup->fu_stats->st_drv.drv_xm_maxpdu++;
394                 fore_seg_dma_free(hxp, m0, nsegs);
395                 KB_FREEALL(m0);
396                 return (NULL);
397         }
398
399         /*
400          * Return the good news to the caller
401          */
402         *segp = nsegs;
403         *lenp = pdulen;
404
405         return (m0);
406 }
407
408
409 /*
410  * Free Transmit Segment Queue DMA addresses
411  * 
412  * Arguments:
413  *      hxp     pointer to host transmit queue entry
414  *      m0      pointer to output PDU buffer chain head
415  *      nsegs   number of processed transmit segments
416  *
417  * Returns:
418  *      none
419  *
420  */
421 static void
422 fore_seg_dma_free(H_xmit_queue *hxp, KBuffer *m0, int nsegs)
423 {
424         KBuffer         *m = m0;
425         H_dma           *sdmap = hxp->hxq_dma;
426         caddr_t         cp;
427         int             i;
428
429         for (i = 0; i < nsegs; i++) {
430                 KB_DATASTART(m, cp, caddr_t);
431                 DMA_FREE_ADDR(cp, *sdmap, KB_LEN(m), 0);
432                 m = KB_NEXT(m);
433                 sdmap++;
434         }
435 }
436