Merge from vendor branch GPERF:
[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.4 2003/08/27 10:35:16 rob 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(cup, cvp, m)
68         Cmn_unit        *cup;
69         Cmn_vcc         *cvp;
70         KBuffer         *m;
71 {
72         Fore_unit       *fup = (Fore_unit *)cup;
73         Fore_vcc        *fvp = (Fore_vcc *)cvp;
74         struct vccb     *vcp;
75         H_xmit_queue    *hxp;
76         Xmit_queue      *cqp;
77         Xmit_descr      *xdp;
78         int             retry, nsegs, pdulen;
79         int             s;
80
81 #ifdef DIAGNOSTIC
82         if (atm_dev_print)
83                 atm_dev_pdu_print(cup, cvp, m, "fore_output");
84 #endif
85
86         vcp = fvp->fv_connvc->cvc_vcc;
87
88         /*
89          * If we're still waiting for activation to finish, delay for
90          * a little while before we toss the PDU
91          */
92         if (fvp->fv_state == CVS_INITED) {
93                 retry = 3;
94                 while (retry-- && (fvp->fv_state == CVS_INITED))
95                         DELAY(1000);
96                 if (fvp->fv_state != CVS_ACTIVE) {
97                         /*
98                          * Activation still hasn't finished, oh well....
99                          */
100                         fup->fu_stats->st_drv.drv_xm_notact++;
101                         vcp->vc_oerrors++;
102                         if (vcp->vc_nif)
103                                 vcp->vc_nif->nif_if.if_oerrors++;
104                         KB_FREEALL(m);
105                         return;
106                 }
107         }
108
109         /*
110          * Queue PDU at end of transmit queue
111          *
112          * If queue is full we'll delay a bit before tossing the PDU
113          */
114         s = splnet();
115         hxp = fup->fu_xmit_tail;
116         if (!((*hxp->hxq_status) & QSTAT_FREE)) {
117
118                 fup->fu_stats->st_drv.drv_xm_full++;
119                 retry = 3;
120                 do {
121                         DELAY(1000);
122
123                         DEVICE_LOCK((Cmn_unit *)fup);
124                         fore_xmit_drain(fup);
125                         DEVICE_UNLOCK((Cmn_unit *)fup);
126
127                 } while (--retry && (!((*hxp->hxq_status) & QSTAT_FREE)));
128
129                 if (!((*hxp->hxq_status) & QSTAT_FREE)) {
130                         /*
131                          * Queue is still full, bye-bye PDU
132                          */
133                         fup->fu_pif.pif_oerrors++;
134                         vcp->vc_oerrors++;
135                         if (vcp->vc_nif)
136                                 vcp->vc_nif->nif_if.if_oerrors++;
137                         KB_FREEALL(m);
138                         (void) splx(s);
139                         return;
140                 }
141         }
142
143         /*
144          * We've got a free transmit queue entry
145          */
146
147         /*
148          * Now build the transmit segment descriptors for this PDU
149          */
150         m = fore_xmit_segment(fup, m, hxp, &nsegs, &pdulen);
151         if (m == NULL) {
152                 /*
153                  * The build failed, buffer chain has been freed
154                  */
155                 vcp->vc_oerrors++;
156                 if (vcp->vc_nif)
157                         vcp->vc_nif->nif_if.if_oerrors++;
158                 (void) splx(s);
159                 return;
160         }
161
162         /*
163          * Set up the descriptor header
164          */
165         xdp = hxp->hxq_descr;
166         xdp->xd_cell_hdr = ATM_HDR_SET(vcp->vc_vpi, vcp->vc_vci, 0, 0);
167         xdp->xd_spec = XDS_SET_SPEC(0, fvp->fv_aal, nsegs, pdulen);
168         xdp->xd_rate = FORE_DEF_RATE;
169
170         /*
171          * Everything is ready to go, so officially claim the host queue
172          * entry and setup the CP-resident queue entry.  The CP will grab
173          * the PDU when the descriptor pointer is set.
174          */
175         fup->fu_xmit_tail = hxp->hxq_next;
176         hxp->hxq_buf = m;
177         hxp->hxq_vcc = fvp;
178         (*hxp->hxq_status) = QSTAT_PENDING;
179         cqp = hxp->hxq_cpelem;
180         cqp->cq_descr = (CP_dma)
181                 CP_WRITE((u_long)hxp->hxq_descr_dma | XMIT_SEGS_TO_BLKS(nsegs));
182
183         (void) splx(s);
184
185         /*
186          * See if there are any completed queue entries
187          */
188         DEVICE_LOCK((Cmn_unit *)fup);
189         fore_xmit_drain(fup);
190         DEVICE_UNLOCK((Cmn_unit *)fup);
191
192         return;
193 }
194
195
196 /*
197  * Build Transmit Segment Descriptors
198  * 
199  * This function will take a supplied buffer chain of data to be transmitted
200  * and build the transmit segment descriptors for the data.  This will include 
201  * the dreaded operation of ensuring that the data for each transmit segment
202  * is full-word aligned and (except for the last segment) is an integral number
203  * of words in length.  If the data isn't already aligned and sized as
204  * required, then the data must be shifted (copied) into place - a sure
205  * performance killer.  Note that we rely on the fact that all buffer data
206  * areas are allocated with (at least) full-word alignments/lengths.
207  *
208  * If any errors are encountered, the buffer chain will be freed.
209  * 
210  * Arguments:
211  *      fup     pointer to device unit
212  *      m       pointer to output PDU buffer chain head
213  *      hxp     pointer to host transmit queue entry
214  *      segp    pointer to return the number of transmit segments
215  *      lenp    pointer to return the pdu length
216  *
217  * Returns:
218  *      m       build successful, pointer to (possibly new) head of 
219  *              output PDU buffer chain
220  *      NULL    build failed, buffer chain freed
221  *
222  */
223 static KBuffer *
224 fore_xmit_segment(fup, m, hxp, segp, lenp)
225         Fore_unit       *fup;
226         KBuffer         *m;
227         H_xmit_queue    *hxp;
228         int             *segp;
229         int             *lenp;
230 {
231         Xmit_descr      *xdp = hxp->hxq_descr;
232         Xmit_seg_descr  *xsp;
233         H_dma           *sdmap;
234         KBuffer         *m0, *m1, *mprev;
235         caddr_t         cp, bfr;
236         void            *dma;
237         int             pdulen, nsegs, len, align;
238         int             compressed = 0;
239
240         m0 = m;
241
242 retry:
243         xsp = xdp->xd_seg;
244         sdmap = hxp->hxq_dma;
245         mprev = NULL;
246         pdulen = 0;
247         nsegs = 0;
248
249         /*
250          * Loop thru each buffer in the chain, performing the necessary
251          * data positioning and then building a segment descriptor for
252          * that data.
253          */
254         while (m) {
255                 /*
256                  * Get rid of any zero-length buffers
257                  */
258                 if (KB_LEN(m) == 0) {
259                         if (mprev) {
260                                 KB_UNLINK(m, mprev, m1);
261                         } else {
262                                 KB_UNLINKHEAD(m, m1);
263                                 m0 = m1;
264                         }
265                         m = m1;
266                         continue;
267                 }
268
269                 /*
270                  * Make sure we don't try to use too many segments
271                  */
272                 if (nsegs >= XMIT_MAX_SEGS) {
273                         /*
274                          * First, free already allocated DMA addresses
275                          */
276                         fore_seg_dma_free(hxp, m0, nsegs);
277
278                         /*
279                          * Try to compress buffer chain (but only once)
280                          */
281                         if (compressed) {
282                                 KB_FREEALL(m0);
283                                 return (NULL);
284                         }
285
286                         fup->fu_stats->st_drv.drv_xm_maxpdu++;
287
288                         m = atm_dev_compress(m0);
289                         if (m == NULL) {
290                                 return (NULL);
291                         }
292
293                         /*
294                          * Build segment descriptors for compressed chain
295                          */
296                         m0 = m;
297                         compressed = 1;
298                         goto retry;
299                 }
300
301                 /*
302                  * Get start of data onto full-word alignment
303                  */
304                 KB_DATASTART(m, cp, caddr_t);
305                 if ((align = ((u_int)cp) & (XMIT_SEG_ALIGN - 1)) != 0) {
306                         /*
307                          * Gotta slide the data up
308                          */
309                         fup->fu_stats->st_drv.drv_xm_segnoal++;
310                         bfr = cp - align;
311                         KM_COPY(cp, bfr, KB_LEN(m));
312                         KB_HEADMOVE(m, -align);
313                 } else {
314                         /*
315                          * Data already aligned
316                          */
317                         bfr = cp;
318                 }
319
320                 /*
321                  * Now work on getting the data length correct
322                  */
323                 len = KB_LEN(m);
324                 while ((align = (len & (XMIT_SEG_ALIGN - 1))) &&
325                        (m1 = KB_NEXT(m))) {
326
327                         /*
328                          * Have to move some data from following buffer(s)
329                          * to word-fill this buffer
330                          */
331                         int     ncopy = MIN(XMIT_SEG_ALIGN - align, KB_LEN(m1));
332
333                         if (ncopy) {
334                                 /*
335                                  * Move data to current buffer
336                                  */
337                                 caddr_t         dest;
338
339                                 fup->fu_stats->st_drv.drv_xm_seglen++;
340                                 KB_DATASTART(m1, cp, caddr_t);
341                                 dest = bfr + len;
342                                 KB_HEADADJ(m1, -ncopy);
343                                 KB_TAILADJ(m, ncopy);
344                                 len += ncopy;
345                                 while (ncopy--) {
346                                         *dest++ = *cp++;
347                                 }
348                         }
349
350                         /*
351                          * If we've drained the buffer, free it
352                          */
353                         if (KB_LEN(m1) == 0) {
354                                 KBuffer         *m2;
355
356                                 KB_UNLINK(m1, m, m2);
357                         }
358                 }
359
360                 /*
361                  * Finally, build the segment descriptor
362                  */
363
364                 /*
365                  * Round last segment to fullword length (if needed)
366                  */
367                 if (len & (XMIT_SEG_ALIGN - 1))
368                         xsp->xsd_len = KB_LEN(m) =
369                                 (len + XMIT_SEG_ALIGN) & ~(XMIT_SEG_ALIGN - 1);
370                 else
371                         xsp->xsd_len = KB_LEN(m) = len;
372
373                 /*
374                  * Get a DMA address for the data
375                  */
376                 dma = DMA_GET_ADDR(bfr, xsp->xsd_len, XMIT_SEG_ALIGN, 0);
377                 if (dma == NULL) {
378                         fup->fu_stats->st_drv.drv_xm_segdma++;
379                         fore_seg_dma_free(hxp, m0, nsegs);
380                         KB_FREEALL(m0);
381                         return (NULL);
382                 }
383
384                 /*
385                  * Now we're really ready to call it a segment
386                  */
387                 *sdmap++ = xsp->xsd_buffer = (H_dma) dma;
388
389                 /*
390                  * Bump counters and get ready for next buffer
391                  */
392                 pdulen += len;
393                 nsegs++;
394                 xsp++;
395                 mprev = m;
396                 m = KB_NEXT(m);
397         }
398
399         /*
400          * Validate PDU length
401          */
402         if (pdulen > XMIT_MAX_PDULEN) {
403                 fup->fu_stats->st_drv.drv_xm_maxpdu++;
404                 fore_seg_dma_free(hxp, m0, nsegs);
405                 KB_FREEALL(m0);
406                 return (NULL);
407         }
408
409         /*
410          * Return the good news to the caller
411          */
412         *segp = nsegs;
413         *lenp = pdulen;
414
415         return (m0);
416 }
417
418
419 /*
420  * Free Transmit Segment Queue DMA addresses
421  * 
422  * Arguments:
423  *      hxp     pointer to host transmit queue entry
424  *      m0      pointer to output PDU buffer chain head
425  *      nsegs   number of processed transmit segments
426  *
427  * Returns:
428  *      none
429  *
430  */
431 static void
432 fore_seg_dma_free(hxp, m0, nsegs)
433         H_xmit_queue    *hxp;
434         KBuffer         *m0;
435         int             nsegs;
436 {
437         KBuffer         *m = m0;
438         H_dma           *sdmap = hxp->hxq_dma;
439         caddr_t         cp;
440         int             i;
441
442         for (i = 0; i < nsegs; i++) {
443                 KB_DATASTART(m, cp, caddr_t);
444                 DMA_FREE_ADDR(cp, *sdmap, KB_LEN(m), 0);
445                 m = KB_NEXT(m);
446                 sdmap++;
447         }
448 }
449