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