11845397facf16c3403ec66263f40acfbf3077fa
[dragonfly.git] / sys / dev / atm / hea / eni_transmit.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/hea/eni_transmit.c,v 1.6 1999/12/21 08:24:35 eivind Exp $
27  *      @(#) $DragonFly: src/sys/dev/atm/hea/eni_transmit.c,v 1.7 2005/06/02 21:36:08 dillon Exp $
28  */
29
30 /*
31  * Efficient ENI Adapter Support
32  * -----------------------------
33  * 
34  * Transmit queue management and PDU output processing
35  *
36  */
37
38
39 #include <netproto/atm/kern_include.h>
40
41 #include "eni_stats.h"
42 #include "eni.h"
43 #include "eni_var.h"
44
45 /*
46  * Make a variable which controls printing of PDUs
47  * as they travel through the driver.
48  */
49 #ifdef  DIAGNOSTIC
50 int     eni_pdu_print = 0;
51 #endif
52
53 /*
54  * Some PCI chipsets do not handle one or more of the 8WORD or
55  * 4WORD DMA transfer sizes. Default to using only 1WORD transfer
56  * sizes unless the user wishes to experiment.
57  *
58  * Make sure that these have to be changed here in this module.
59  */
60 #define DMA_USE_8WORD
61 #define DMA_USE_4WORD
62
63 /*
64  * Create a DMA list entry
65  *
66  * DMA entries consist of a control word and a physical address.
67  * Control words are comprised of a DMA type, a count of type transfers
68  * to occur, and a variable which for TX requests is the TX channel
69  * number and for RX requests is the VCC number.
70  *
71  * Arguments:
72  *      eup             pointer to unit structure
73  *      rx              set if receiving
74  *      dma_list        pointer to DMA list structure
75  *      list_size       length of DMA list structure
76  *      idx             pointer to current list entry
77  *      val             TX channel or RX vcc
78  *      addr            virtual DMA address of data buffer
79  *      size            size in bytes of DMA request to be built
80  *
81  * Returns:
82  *      dma_list        updated with new entries
83  *      idx             points to next list entry
84  *      -1              no room in DMA list structure or DMA_GET_ADDR failed
85  */
86 int
87 eni_set_dma ( eup, rx, dma_list, list_size, idx, val, addr, size )
88 Eni_unit *eup;
89 u_long  *dma_list;
90 int     list_size;
91 long    *idx;
92 int     val;
93 u_long  addr;
94 int     size;
95 {
96         int     dsize;          /* Size of current DMA request */
97
98         /*
99          * Round up to multiple of word and convert to number
100          * of words rather then number of bytes.
101          */
102         size = ( size + 3 ) >> 2;
103
104 #ifdef  DMA_USE_8WORD
105         /*
106          * Check for room in DMA list - we need two entires
107          */
108         if ( *idx + 2 >= list_size )
109                 return ( -1 );
110
111         /*
112          * Here is the big win. Move as much data possible with
113          * n 8WORD DMAs.
114          */
115         /*
116          * Check if we can do one or more 8WORD DMAs
117          */
118         dsize = size & ~7;
119         if ( dsize ) {
120                 dma_list[(*idx)++] = ( dsize >> 3 ) << DMA_COUNT_SHIFT |
121                         val << DMA_VCC_SHIFT | DMA_8WORD;
122                 dma_list[*idx] = (u_long)DMA_GET_ADDR ( addr, dsize, 0, 0 );
123                 if ( dma_list[*idx] == 0 ) {
124                         if ( rx )
125                                 eup->eu_stats.eni_st_drv.drv_rv_segdma++;
126                         else
127                                 eup->eu_stats.eni_st_drv.drv_xm_segdma++;
128                         return ( -1 );          /* DMA_GET_ADDR failed */
129                 } else
130                         (*idx)++;               /* increment index */
131                 /*
132                  * Adjust addr and size
133                  */
134                 addr += dsize << 2;
135                 size &= 7;
136         }
137 #endif  /* DMA_USE_8WORD */
138
139 #ifdef  DMA_USE_4WORD
140         /*
141          * Check for room in DMA list - we need two entries
142          */
143         if ( *idx + 2 >= list_size )
144                 return ( -1 );
145
146         /*
147          * Kindof a tossup from this point on. Since we hacked as many 
148          * 8WORD DMAs off as possible, we are left with 0-7 words
149          * of remaining data. We could do upto one 4WORD with 0-3
150          * words left, or upto three 2WORDS with 0-1 words left,
151          * or upto seven WORDS with nothing left. Someday we should
152          * experiment with performance and see if any particular
153          * combination is a better win then some other...
154          */
155         /*
156          * Check if we can do one or more 4WORD DMAs
157          */
158         dsize = size & ~3;
159         if ( dsize ) {
160                 dma_list[(*idx)++] = ( dsize >> 2 ) << DMA_COUNT_SHIFT |
161                         val << DMA_VCC_SHIFT | DMA_4WORD;
162                 dma_list[*idx] = (u_long)DMA_GET_ADDR ( addr, dsize, 0, 0 );
163                 if ( dma_list[*idx] == 0 ) {
164                         if ( rx )
165                                 eup->eu_stats.eni_st_drv.drv_rv_segdma++;
166                         else
167                                 eup->eu_stats.eni_st_drv.drv_xm_segdma++;
168                         return ( -1 );          /* DMA_GET_ADDR failed */
169                 } else
170                         (*idx)++;               /* increment index */
171                 /*
172                  * Adjust addr and size
173                  */
174                 addr += dsize << 2;
175                 size &= 3;
176         }
177 #endif  /* DMA_USE_4WORD */
178
179         /*
180          * Check for room in DMA list - we need two entries
181          */
182         if ( *idx + 2 >= list_size )
183                 return ( -1 );
184
185         /*
186          * Hard to know if one 2WORD and 0/1 WORD DMA would be better
187          * then 2/3 WORD DMAs. For now, skip 2WORD DMAs in favor of
188          * WORD DMAs.
189          */
190
191         /*
192          * Finish remaining size a 1WORD DMAs
193          */
194         if ( size ) {
195                 dma_list[(*idx)++] = ( size ) << DMA_COUNT_SHIFT |
196                         val << DMA_VCC_SHIFT | DMA_WORD;
197                 dma_list[*idx] = (u_long)DMA_GET_ADDR ( addr, size, 0, 0 );
198                 if ( dma_list[*idx] == 0 ) {
199                         if ( rx )
200                                 eup->eu_stats.eni_st_drv.drv_rv_segdma++;
201                         else
202                                 eup->eu_stats.eni_st_drv.drv_xm_segdma++;
203                         return ( -1 );          /* DMA_GET_ADDR failed */
204                 } else
205                         (*idx)++;               /* increment index */
206         }
207
208         /*
209          * Inserted descriptor okay
210          */
211         return 0;
212 }
213
214 /*
215  * Drain Transmit queue
216  *
217  * As PDUs are given to the adapter to be transmitted, we
218  * place them into a private ifqueue so that we can free
219  * any resources AFTER we know they've been successfully DMAed.
220  * As part of the output processing, we record the PDUs start
221  * and stop entries in the DMA list, and prevent wrapping. When
222  * we pull the top element off, we simply check that the current
223  * DMA location is outside this PDU and if so, it's okay to free
224  * things.
225  *
226  * PDUs are always in ascending order in the queue.
227  *
228  * Arguments:
229  *      eup             pointer to device unit structure
230  *
231  * Returns:
232  *      none
233  *
234  */
235 void
236 eni_xmit_drain ( eup )
237         Eni_unit *eup;
238 {
239         KBuffer         *m;
240         Eni_vcc         *evp;
241         struct vccb     *vcp;
242         u_long          pdulen;
243         u_long          start, stop;
244         u_long          dmap;
245
246         crit_enter();
247
248         /*
249          * Pull the top element (PDU) off
250          */
251         IF_DEQUEUE ( &eup->eu_txqueue, m );
252         /*
253          * As long as there are valid elements
254          */
255         while ( m ) {
256                 u_long *up;
257
258                 /*
259                  * Find start of buffer
260                  */
261                 KB_DATASTART ( m, up, u_long * );
262
263                 /*
264                  * First word is the VCC for this PDU
265                  */
266                 /*
267                  * NOTE: There is a potential problem here in that
268                  * if the VCC is closed after this buffer was transmitted
269                  * but before we get here, that while evp is non-null,
270                  * it will not reference a valid vccb. We need to either
271                  * delay closing the VCC until all references are removed
272                  * from the drain stacks, actually go through the drain
273                  * stacks and remove any references, or find someway of
274                  * indicating that this vccb is nolonger usable.
275                  */
276                 evp = (Eni_vcc *)*up++;
277                 /*
278                  * Second word is the start and stop DMA pointers
279                  */
280                 start = *up >> 16;
281                 stop = *up++ & 0xffff;
282                 /*
283                  * Find out where the TX engine is at
284                  */
285                 dmap = eup->eu_midway[MIDWAY_TX_RD];
286                 /*
287                  * Check to see if TX engine has processed this
288                  * PDU yet. Remember that everything is circular
289                  * and that stop might be less than start numerically.
290                  */
291                 if ( start > stop ) {
292                     if ( !(dmap >= stop && dmap < start) ) {
293                         /*
294                          * Haven't finished this PDU yet - replace
295                          * it as the head of list.
296                          */
297                         IF_PREPEND ( &eup->eu_txqueue, m );
298                         /*
299                          * If this one isn't done, none of the others
300                          * are either.
301                          */
302                         crit_exit();
303                         return;
304                     }
305                 } else {
306                     if ( dmap < stop && dmap >= start ) {
307                         /*
308                          * Haven't finished this PDU yet - replace
309                          * it as the head of list.
310                          */
311                         IF_PREPEND ( &eup->eu_txqueue, m );
312                         /*
313                          * If this one isn't done, none of the others
314                          * are either.
315                          */
316                         crit_exit();
317                         return;
318                     }
319                 }
320
321                 /*
322                  * Count the PDU stats for this interface
323                  */
324                 eup->eu_pif.pif_opdus++;
325                 /*
326                  * Third word is PDU length from eni_output().
327                  */
328                 pdulen = *up++;
329                 eup->eu_txfirst = (eup->eu_txfirst + *up) &
330                         (eup->eu_txsize - 1);
331                 eup->eu_pif.pif_obytes += pdulen;
332
333                 /*
334                  * Now lookup the VCC entry and counts the stats for
335                  * this VC.
336                  */
337                 if ( evp ) {
338                     vcp = evp->ev_connvc->cvc_vcc;
339                     if ( vcp ) {
340                         vcp->vc_opdus++;
341                         vcp->vc_obytes += pdulen;
342                         /*
343                          * If we also have a network interface, count the PDU
344                          * there also.
345                          */
346                         if ( vcp->vc_nif ) {
347                                 vcp->vc_nif->nif_obytes += pdulen;
348                                 vcp->vc_nif->nif_if.if_opackets++;
349                                 vcp->vc_nif->nif_if.if_obytes += pdulen;
350                         }
351                     }
352                 }
353                 /*
354                  * Free the buffer chain
355                  */
356                 KB_FREEALL ( m );
357
358                 /*
359                  * Advance DMA write okay pointer
360                  */
361                 eup->eu_txdmawr = stop;
362
363                 /*
364                  * Look for next completed transmit PDU
365                  */
366                 IF_DEQUEUE ( &eup->eu_txqueue, m );
367         }
368         /*
369          * We've drained the queue...
370          */
371         crit_exit();
372 }
373
374 /*
375  * Output a PDU
376  *
377  * This function is called via the common driver code after receiving a
378  * stack *_DATA* command. The common code has already validated most of
379  * the request so we just need to check a few more ENI-specific details.
380  * Then we just build a segmentation structure for the PDU and place the
381  * address into the DMA_Transmit_queue.
382  *
383  * Arguments:
384  *      cup             pointer to device common unit
385  *      cvp             pointer to common VCC entry
386  *      m               pointer to output PDU buffer chain head
387  *
388  * Returns:
389  *      none
390  *
391  */
392 void
393 eni_output ( cup, cvp, m )
394         Cmn_unit        *cup;
395         Cmn_vcc         *cvp;
396         KBuffer         *m;
397 {
398         Eni_unit        *eup = (Eni_unit *)cup;
399         Eni_vcc         *evp = (Eni_vcc *)cvp;
400         int             pdulen = 0;
401         u_long          size;
402         u_long          buf_avail;
403         u_long          dma_rd, dma_wr;
404         u_long          dma[TEMP_DMA_SIZE];
405         int             aal5, i;
406         long            j;
407         u_long          dma_avail;
408         u_long          dma_start;
409         Eni_mem         tx_send;
410         u_long          *up;
411         KBuffer         *m0 = m, *m1, *mprev = NULL;
412         caddr_t         cp, bfr;
413         u_int           len, align;
414         int             compressed = 0;
415
416 #ifdef  DIAGNOSTIC
417         if ( eni_pdu_print )
418                 atm_dev_pdu_print ( cup, cvp, m, "eni output" );
419 #endif
420
421         /*
422          * Re-entry point for after buffer compression (if needed)
423          */
424 retry:
425
426         /*
427          * We can avoid traversing the buffer list twice by building
428          * the middle (minus header and trailer) dma list at the
429          * same time we massage address and size alignments. Since
430          * this list remains local until we determine we've enough
431          * room, we're not going to trash anything by not checking
432          * sizes, etc. yet. Skip first entry to be used later to skip
433          * descriptor word.
434          */
435         j = 2;
436         /*
437          * Do data positioning for address and length alignment
438          */
439         while ( m ) {
440                 u_long  buf_addr;       /* For passing addr to eni_set_dma() */
441
442                 /*
443                  * Get rid of any zero length buffers
444                  */
445                 if ( KB_LEN ( m ) == 0 ) {
446                         if ( mprev ) {
447                                 KB_UNLINK ( m, mprev, m1 );
448                         } else {
449                                 KB_UNLINKHEAD ( m, m1 );
450                                 m0 = m1;
451                         }
452                         m = m1;
453                         continue;
454                 }
455                 /*
456                  * Get start of data onto full-word alignment
457                  */
458                 KB_DATASTART ( m, cp, caddr_t );
459                 if ((align = ((u_int)cp) & (sizeof(u_long)-1)) != 0) {
460                         /*
461                          * Gotta slide the data up
462                          */
463                         eup->eu_stats.eni_st_drv.drv_xm_segnoal++;
464                         bfr = cp - align;
465                         KM_COPY ( cp, bfr, KB_LEN ( m ) );
466                         KB_HEADMOVE ( m, -align );
467                 } else {
468                         /*
469                          * Data already aligned
470                          */
471                         bfr = cp;
472                 }
473                 /*
474                  * Now work on getting the data length correct
475                  */
476                 len = KB_LEN ( m );
477                 while ( ( align = ( len & (sizeof(u_long)-1))) &&
478                         (m1 = KB_NEXT ( m ) ) ) {
479
480                         /*
481                          * Have to move some data from following buffer(s)
482                          * to word-fill this buffer
483                          */
484                         u_int ncopy = MIN ( sizeof(u_long) - align,
485                                 KB_LEN ( m1 ) );
486
487                         if ( ncopy ) {
488                                 /*
489                                  * Move data to current buffer
490                                  */
491                                 caddr_t dest;
492
493                                 eup->eu_stats.eni_st_drv.drv_xm_seglen++;
494                                 KB_DATASTART ( m1, cp, caddr_t );
495                                 dest = bfr + len;
496                                 KB_HEADADJ ( m1, -ncopy );
497                                 KB_TAILADJ ( m, ncopy );
498                                 len += ncopy;
499                                 while ( ncopy-- ) {
500                                         *dest++ = *cp++;
501                                 }
502                         }
503
504                         /*
505                          * If we've drained the buffer, free it
506                          */
507                         if ( KB_LEN ( m1 ) == 0 ) {
508                                 KBuffer *m2;
509
510                                 KB_UNLINK ( m1, m, m2 );
511                         }
512                 }
513
514                 /*
515                  * Address and size are now aligned. Build dma list
516                  * using TX channel 0. Also, round length up to a word
517                  * size which should only effect the last buffer in the
518                  * chain. This works because the PDU length is maintained
519                  * separately and we're not really adjusting the buffer's
520                  * idea of its length.
521                  */
522                 KB_DATASTART ( m, buf_addr, u_long );
523                 if ( eni_set_dma ( eup, 0, dma, TEMP_DMA_SIZE, &j, 0,
524                     buf_addr, KB_LEN ( m ) ) < 0 ) {
525                         /*
526                          * Failed to build DMA list. First, we'll try to
527                          * compress the buffer chain into a smaller number
528                          * of buffers. After compressing, we'll try to send
529                          * the new buffer chain. If we still fail, then
530                          * we'll drop the pdu.
531                          */
532                         if ( compressed ) {
533 #ifdef  DO_LOG
534                                 log ( LOG_ERR,
535                                         "eni_output: eni_set_dma failed\n" );
536 #endif
537                                 eup->eu_pif.pif_oerrors++;
538                                 KB_FREEALL ( m0 );
539                                 return;
540                         }
541
542                         eup->eu_stats.eni_st_drv.drv_xm_maxpdu++;
543
544                         m = atm_dev_compress ( m0 );
545                         if ( m == NULL ) {
546 #ifdef  DO_LOG
547                                 log ( LOG_ERR,
548                                     "eni_output: atm_dev_compress() failed\n" );
549 #endif
550                                 eup->eu_pif.pif_oerrors++;
551                                 return;
552                         }
553
554                         /*
555                          * Reset to new head of buffer chain
556                          */
557                         m0 = m;
558
559                         /*
560                          * Indicate we've been through here
561                          */
562                         compressed = 1;
563
564                         /*
565                          * Retry to build the DMA descriptors for the newly
566                          *  compressed buffer chain
567                          */
568                         goto retry;
569
570                 }
571
572                 /*
573                  * Now count the length
574                  */
575                 pdulen += KB_LEN ( m );
576
577                 /*
578                  * Bump counters and get ready for next buffer
579                  */
580                 mprev = m;
581                 m = KB_NEXT ( m );
582         }
583
584         /*
585          * Get a buffer to use in a private queue so that we can
586          * reclaim resources after the DMA has finished.
587          */
588         KB_ALLOC ( m, ENI_SMALL_BSIZE, KB_F_NOWAIT, KB_T_DATA );
589         if ( m ) {
590                 /*
591                  * Link the PDU onto our new head
592                  */
593                 KB_NEXT ( m ) = m0;
594         } else {
595                 /*
596                  * Drop this PDU and let the sender try again.
597                  */
598                 eup->eu_stats.eni_st_drv.drv_xm_norsc++;
599 #ifdef  DO_LOG
600                 log(LOG_ERR, "eni_output: Unable to allocate drain buffer.\n");
601 #endif
602                 eup->eu_pif.pif_oerrors++;
603                 KB_FREEALL ( m0 );
604                 return;
605         }
606
607         crit_enter();
608
609         /*
610          * Calculate size of buffer necessary to store PDU. If this
611          * is an AAL5 PDU, we'll need to know where to stuff the length
612          * value in the trailer.
613          */
614         /*
615          * AAL5 PDUs need an extra two words for control/length and
616          * CRC. Check for AAL5 and add requirements here.
617          */
618         if ((aal5 = (evp->ev_connvc->cvc_attr.aal.type == ATM_AAL5)) != 0)
619                 size = pdulen + 2 * sizeof(long);
620         else
621                 size = pdulen;
622         /*
623          * Pad to next complete cell boundary
624          */
625         size += (BYTES_PER_CELL - 1);
626         size -= size % BYTES_PER_CELL;
627         /*
628          * Convert size to words and add 2 words overhead for every
629          * PDU (descriptor and cell header).
630          */
631         size = (size >> 2) + 2;
632
633         /*
634          * First, check to see if there's enough buffer space to
635          * store the PDU. We do this by checking to see if the size
636          * required crosses the eu_txfirst pointer.  However, we don't
637          * want to exactly fill the buffer, because we won't be able to
638          * distinguish between a full and empty buffer.
639          */
640         if ( eup->eu_txpos == eup->eu_txfirst )
641                 buf_avail = eup->eu_txsize;
642         else
643             if ( eup->eu_txpos > eup->eu_txfirst )
644                 buf_avail = eup->eu_txsize - ( eup->eu_txpos - eup->eu_txfirst );
645             else
646                 buf_avail = eup->eu_txfirst - eup->eu_txpos;
647
648         if ( size >= buf_avail )
649         {
650                 /*
651                  * No buffer space in the adapter to store this PDU.
652                  * Drop PDU and return.
653                  */
654                 eup->eu_stats.eni_st_drv.drv_xm_nobuf++;
655 #ifdef  DO_LOG
656                 log ( LOG_ERR,
657                         "eni_output: not enough room in buffer\n" );
658 #endif
659                 eup->eu_pif.pif_oerrors++;
660                 KB_FREEALL ( m );
661                 crit_exit();
662                 return;
663         }
664
665         /*
666          * Find out where current DMA pointers are at
667          */
668         dma_start = dma_wr = eup->eu_midway[MIDWAY_TX_WR];
669         dma_rd = eup->eu_midway[MIDWAY_TX_RD];
670
671         /*
672          * Figure out how much DMA room we have available
673          */
674         if ( dma_rd == dma_wr ) {               /* Queue is empty */
675                 dma_avail = DMA_LIST_SIZE;
676         } else {
677                 dma_avail = ( dma_rd + DMA_LIST_SIZE - dma_wr )
678                     & ( DMA_LIST_SIZE - 1 );
679         }
680         /*
681          * Check to see if we can describe this PDU or if we're:
682          * out of room, will wrap past recovered resources.
683          */
684         if ( dma_avail < (j / 2 + 4) ||
685             ( dma_wr + (j / 2 + 4) > eup->eu_txdmawr + DMA_LIST_SIZE ) ) {
686                 /*
687                  * No space to insert DMA list into queue. Drop this PDU.
688                  */
689                 eup->eu_stats.eni_st_drv.drv_xm_nodma++;
690 #ifdef  DO_LOG
691                 log ( LOG_ERR,
692                         "eni_output: not enough room in DMA queue\n" );
693 #endif
694                 eup->eu_pif.pif_oerrors++;
695                 KB_FREEALL( m );
696                 crit_exit();
697                 return;
698         }
699
700         /*
701          * Create DMA descriptor for header. There is a descriptor word
702          * and also a cell header word which we'll set manually.
703          */
704         dma[0] = (((int)(eup->eu_txpos + 2) & (eup->eu_txsize-1)) <<
705             DMA_COUNT_SHIFT) | DMA_JK;
706         dma[1] = 0;
707
708         /*
709          * JK for AAL5 trailer. Set END bit as well.
710          */
711         if ( aal5 ) {
712             dma[j++] = (((int)(eup->eu_txpos+size) & (eup->eu_txsize-1)) <<
713                 DMA_COUNT_SHIFT) | DMA_END_BIT | DMA_JK;
714             dma[j++] = 0;
715         } else {
716                 dma[j-2] |= DMA_END_BIT;        /* Backup and set END bit */
717         }
718
719         /*
720          * Find out where in adapter memory this TX buffer starts.
721          */
722         tx_send = (Eni_mem)
723             ((((int)eup->eu_midway[MIDWAY_TXPLACE] & 0x7ff) << ENI_LOC_PREDIV) +
724                     (int)eup->eu_ram);
725
726         /*
727          * Set descriptor word
728          */
729         tx_send[eup->eu_txpos] =
730                 (MIDWAY_UNQ_ID << 28) | (aal5 ? 1 << 27 : 0)
731                         | (size / WORDS_PER_CELL);
732         /*
733          * Set cell header
734          */
735         tx_send[(eup->eu_txpos+1)&(eup->eu_txsize-1)] = 
736                 evp->ev_connvc->cvc_vcc->vc_vci << 4;
737
738         /*
739          * We've got all our resources, count the stats
740          */
741         if ( aal5 ) {
742                 /*
743                  * If this is an AAL5 PDU, we need to set the length
744                  */
745                 tx_send[(eup->eu_txpos+size-2) &
746                         (eup->eu_txsize-1)] = pdulen;
747                 /*
748                  * Increment AAL5 stats
749                  */
750                 eup->eu_stats.eni_st_aal5.aal5_pdu_xmit++;
751                 eup->eu_stats.eni_st_aal5.aal5_xmit += (size - 2) / WORDS_PER_CELL;
752         } else {
753                 /*
754                  * Increment AAL0 stats
755                  */
756                 eup->eu_stats.eni_st_aal0.aal0_xmit += (size - 2) / WORDS_PER_CELL;
757         }
758         /*
759          * Increment ATM stats
760          */
761         eup->eu_stats.eni_st_atm.atm_xmit += (size - 2) / WORDS_PER_CELL;
762
763         /*
764          * Store the DMA list
765          */
766         j = j >> 1;
767         for ( i = 0; i < j; i++ ) {
768                 eup->eu_txdma[dma_wr*2] = dma[i*2];
769                 eup->eu_txdma[dma_wr*2+1] = dma[i*2+1];
770                 dma_wr = (dma_wr+1) & (DMA_LIST_SIZE-1);
771         }
772
773         /*
774          * Build drain buffer
775          *
776          * We toss four words in to help keep track of this
777          * PDU. The first is a pointer to the VC control block
778          * so we can find which VCI this went out on, the second
779          * is the start and stop pointers for the DMA list which
780          * describes this PDU, the third is the PDU length
781          * since we'll want to know that for stats gathering,
782          * and the fourth is the number of DMA words.
783          */
784         KB_DATASTART ( m, up, u_long * );
785         *up++ = (u_long)cvp;
786         *up++ = dma_start << 16 | dma_wr;
787         *up++ = pdulen;
788         *up = size;
789
790         /*
791          * Set length of our buffer
792          */
793         KB_LEN ( m ) = 4 * sizeof ( long );
794
795         /*
796          * Place buffers onto transmit queue for draining
797          */
798         IF_ENQUEUE ( &eup->eu_txqueue, m );
799
800         /*
801          * Update next word to be stored
802          */
803         eup->eu_txpos = ((eup->eu_txpos + size) & (eup->eu_txsize - 1));
804
805         /*
806          * Update MIDWAY_TX_WR pointer
807          */
808         eup->eu_midway[MIDWAY_TX_WR] = dma_wr;
809         
810         crit_exit();
811 }
812