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