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