Remove compat code for anything, but DragonFly.
[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.6 2005/02/01 00:51:49 joerg 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         int             s = splimp();
246
247         /*
248          * Pull the top element (PDU) off
249          */
250         IF_DEQUEUE ( &eup->eu_txqueue, m );
251         /*
252          * As long as there are valid elements
253          */
254         while ( m ) {
255                 u_long *up;
256
257                 /*
258                  * Find start of buffer
259                  */
260                 KB_DATASTART ( m, up, u_long * );
261
262                 /*
263                  * First word is the VCC for this PDU
264                  */
265                 /*
266                  * NOTE: There is a potential problem here in that
267                  * if the VCC is closed after this buffer was transmitted
268                  * but before we get here, that while evp is non-null,
269                  * it will not reference a valid vccb. We need to either
270                  * delay closing the VCC until all references are removed
271                  * from the drain stacks, actually go through the drain
272                  * stacks and remove any references, or find someway of
273                  * indicating that this vccb is nolonger usable.
274                  */
275                 evp = (Eni_vcc *)*up++;
276                 /*
277                  * Second word is the start and stop DMA pointers
278                  */
279                 start = *up >> 16;
280                 stop = *up++ & 0xffff;
281                 /*
282                  * Find out where the TX engine is at
283                  */
284                 dmap = eup->eu_midway[MIDWAY_TX_RD];
285                 /*
286                  * Check to see if TX engine has processed this
287                  * PDU yet. Remember that everything is circular
288                  * and that stop might be less than start numerically.
289                  */
290                 if ( start > stop ) {
291                     if ( !(dmap >= stop && dmap < start) ) {
292                         /*
293                          * Haven't finished this PDU yet - replace
294                          * it as the head of list.
295                          */
296                         IF_PREPEND ( &eup->eu_txqueue, m );
297                         /*
298                          * If this one isn't done, none of the others
299                          * are either.
300                          */
301                         (void) splx(s);
302                         return;
303                     }
304                 } else {
305                     if ( dmap < stop && dmap >= start ) {
306                         /*
307                          * Haven't finished this PDU yet - replace
308                          * it as the head of list.
309                          */
310                         IF_PREPEND ( &eup->eu_txqueue, m );
311                         /*
312                          * If this one isn't done, none of the others
313                          * are either.
314                          */
315                         (void) splx(s);
316                         return;
317                     }
318                 }
319
320                 /*
321                  * Count the PDU stats for this interface
322                  */
323                 eup->eu_pif.pif_opdus++;
324                 /*
325                  * Third word is PDU length from eni_output().
326                  */
327                 pdulen = *up++;
328                 eup->eu_txfirst = (eup->eu_txfirst + *up) &
329                         (eup->eu_txsize - 1);
330                 eup->eu_pif.pif_obytes += pdulen;
331
332                 /*
333                  * Now lookup the VCC entry and counts the stats for
334                  * this VC.
335                  */
336                 if ( evp ) {
337                     vcp = evp->ev_connvc->cvc_vcc;
338                     if ( vcp ) {
339                         vcp->vc_opdus++;
340                         vcp->vc_obytes += pdulen;
341                         /*
342                          * If we also have a network interface, count the PDU
343                          * there also.
344                          */
345                         if ( vcp->vc_nif ) {
346                                 vcp->vc_nif->nif_obytes += pdulen;
347                                 vcp->vc_nif->nif_if.if_opackets++;
348                                 vcp->vc_nif->nif_if.if_obytes += pdulen;
349                         }
350                     }
351                 }
352                 /*
353                  * Free the buffer chain
354                  */
355                 KB_FREEALL ( m );
356
357                 /*
358                  * Advance DMA write okay pointer
359                  */
360                 eup->eu_txdmawr = stop;
361
362                 /*
363                  * Look for next completed transmit PDU
364                  */
365                 IF_DEQUEUE ( &eup->eu_txqueue, m );
366         }
367         /*
368          * We've drained the queue...
369          */
370         (void) splx(s);
371         return;
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             s, s2;
401         int             pdulen = 0;
402         u_long          size;
403         u_long          buf_avail;
404         u_long          dma_rd, dma_wr;
405         u_long          dma[TEMP_DMA_SIZE];
406         int             aal5, i;
407         long            j;
408         u_long          dma_avail;
409         u_long          dma_start;
410         Eni_mem         tx_send;
411         u_long          *up;
412         KBuffer         *m0 = m, *m1, *mprev = NULL;
413         caddr_t         cp, bfr;
414         u_int           len, align;
415         int             compressed = 0;
416
417 #ifdef  DIAGNOSTIC
418         if ( eni_pdu_print )
419                 atm_dev_pdu_print ( cup, cvp, m, "eni output" );
420 #endif
421
422         /*
423          * Re-entry point for after buffer compression (if needed)
424          */
425 retry:
426
427         /*
428          * We can avoid traversing the buffer list twice by building
429          * the middle (minus header and trailer) dma list at the
430          * same time we massage address and size alignments. Since
431          * this list remains local until we determine we've enough
432          * room, we're not going to trash anything by not checking
433          * sizes, etc. yet. Skip first entry to be used later to skip
434          * descriptor word.
435          */
436         j = 2;
437         /*
438          * Do data positioning for address and length alignment
439          */
440         while ( m ) {
441                 u_long  buf_addr;       /* For passing addr to eni_set_dma() */
442
443                 /*
444                  * Get rid of any zero length buffers
445                  */
446                 if ( KB_LEN ( m ) == 0 ) {
447                         if ( mprev ) {
448                                 KB_UNLINK ( m, mprev, m1 );
449                         } else {
450                                 KB_UNLINKHEAD ( m, m1 );
451                                 m0 = m1;
452                         }
453                         m = m1;
454                         continue;
455                 }
456                 /*
457                  * Get start of data onto full-word alignment
458                  */
459                 KB_DATASTART ( m, cp, caddr_t );
460                 if ((align = ((u_int)cp) & (sizeof(u_long)-1)) != 0) {
461                         /*
462                          * Gotta slide the data up
463                          */
464                         eup->eu_stats.eni_st_drv.drv_xm_segnoal++;
465                         bfr = cp - align;
466                         KM_COPY ( cp, bfr, KB_LEN ( m ) );
467                         KB_HEADMOVE ( m, -align );
468                 } else {
469                         /*
470                          * Data already aligned
471                          */
472                         bfr = cp;
473                 }
474                 /*
475                  * Now work on getting the data length correct
476                  */
477                 len = KB_LEN ( m );
478                 while ( ( align = ( len & (sizeof(u_long)-1))) &&
479                         (m1 = KB_NEXT ( m ) ) ) {
480
481                         /*
482                          * Have to move some data from following buffer(s)
483                          * to word-fill this buffer
484                          */
485                         u_int ncopy = MIN ( sizeof(u_long) - align,
486                                 KB_LEN ( m1 ) );
487
488                         if ( ncopy ) {
489                                 /*
490                                  * Move data to current buffer
491                                  */
492                                 caddr_t dest;
493
494                                 eup->eu_stats.eni_st_drv.drv_xm_seglen++;
495                                 KB_DATASTART ( m1, cp, caddr_t );
496                                 dest = bfr + len;
497                                 KB_HEADADJ ( m1, -ncopy );
498                                 KB_TAILADJ ( m, ncopy );
499                                 len += ncopy;
500                                 while ( ncopy-- ) {
501                                         *dest++ = *cp++;
502                                 }
503                         }
504
505                         /*
506                          * If we've drained the buffer, free it
507                          */
508                         if ( KB_LEN ( m1 ) == 0 ) {
509                                 KBuffer *m2;
510
511                                 KB_UNLINK ( m1, m, m2 );
512                         }
513                 }
514
515                 /*
516                  * Address and size are now aligned. Build dma list
517                  * using TX channel 0. Also, round length up to a word
518                  * size which should only effect the last buffer in the
519                  * chain. This works because the PDU length is maintained
520                  * separately and we're not really adjusting the buffer's
521                  * idea of its length.
522                  */
523                 KB_DATASTART ( m, buf_addr, u_long );
524                 if ( eni_set_dma ( eup, 0, dma, TEMP_DMA_SIZE, &j, 0,
525                     buf_addr, KB_LEN ( m ) ) < 0 ) {
526                         /*
527                          * Failed to build DMA list. First, we'll try to
528                          * compress the buffer chain into a smaller number
529                          * of buffers. After compressing, we'll try to send
530                          * the new buffer chain. If we still fail, then
531                          * we'll drop the pdu.
532                          */
533                         if ( compressed ) {
534 #ifdef  DO_LOG
535                                 log ( LOG_ERR,
536                                         "eni_output: eni_set_dma failed\n" );
537 #endif
538                                 eup->eu_pif.pif_oerrors++;
539                                 KB_FREEALL ( m0 );
540                                 return;
541                         }
542
543                         eup->eu_stats.eni_st_drv.drv_xm_maxpdu++;
544
545                         m = atm_dev_compress ( m0 );
546                         if ( m == NULL ) {
547 #ifdef  DO_LOG
548                                 log ( LOG_ERR,
549                                     "eni_output: atm_dev_compress() failed\n" );
550 #endif
551                                 eup->eu_pif.pif_oerrors++;
552                                 return;
553                         }
554
555                         /*
556                          * Reset to new head of buffer chain
557                          */
558                         m0 = m;
559
560                         /*
561                          * Indicate we've been through here
562                          */
563                         compressed = 1;
564
565                         /*
566                          * Retry to build the DMA descriptors for the newly
567                          *  compressed buffer chain
568                          */
569                         goto retry;
570
571                 }
572
573                 /*
574                  * Now count the length
575                  */
576                 pdulen += KB_LEN ( m );
577
578                 /*
579                  * Bump counters and get ready for next buffer
580                  */
581                 mprev = m;
582                 m = KB_NEXT ( m );
583         }
584
585         /*
586          * Get a buffer to use in a private queue so that we can
587          * reclaim resources after the DMA has finished.
588          */
589         KB_ALLOC ( m, ENI_SMALL_BSIZE, KB_F_NOWAIT, KB_T_DATA );
590         if ( m ) {
591                 /*
592                  * Link the PDU onto our new head
593                  */
594                 KB_NEXT ( m ) = m0;
595         } else {
596                 /*
597                  * Drop this PDU and let the sender try again.
598                  */
599                 eup->eu_stats.eni_st_drv.drv_xm_norsc++;
600 #ifdef  DO_LOG
601                 log(LOG_ERR, "eni_output: Unable to allocate drain buffer.\n");
602 #endif
603                 eup->eu_pif.pif_oerrors++;
604                 KB_FREEALL ( m0 );
605                 return;
606         }
607
608         s = splnet();
609
610         /*
611          * Calculate size of buffer necessary to store PDU. If this
612          * is an AAL5 PDU, we'll need to know where to stuff the length
613          * value in the trailer.
614          */
615         /*
616          * AAL5 PDUs need an extra two words for control/length and
617          * CRC. Check for AAL5 and add requirements here.
618          */
619         if ((aal5 = (evp->ev_connvc->cvc_attr.aal.type == ATM_AAL5)) != 0)
620                 size = pdulen + 2 * sizeof(long);
621         else
622                 size = pdulen;
623         /*
624          * Pad to next complete cell boundary
625          */
626         size += (BYTES_PER_CELL - 1);
627         size -= size % BYTES_PER_CELL;
628         /*
629          * Convert size to words and add 2 words overhead for every
630          * PDU (descriptor and cell header).
631          */
632         size = (size >> 2) + 2;
633
634         /*
635          * First, check to see if there's enough buffer space to
636          * store the PDU. We do this by checking to see if the size
637          * required crosses the eu_txfirst pointer.  However, we don't
638          * want to exactly fill the buffer, because we won't be able to
639          * distinguish between a full and empty buffer.
640          */
641         if ( eup->eu_txpos == eup->eu_txfirst )
642                 buf_avail = eup->eu_txsize;
643         else
644             if ( eup->eu_txpos > eup->eu_txfirst )
645                 buf_avail = eup->eu_txsize - ( eup->eu_txpos - eup->eu_txfirst );
646             else
647                 buf_avail = eup->eu_txfirst - eup->eu_txpos;
648
649         if ( size >= buf_avail )
650         {
651                 /*
652                  * No buffer space in the adapter to store this PDU.
653                  * Drop PDU and return.
654                  */
655                 eup->eu_stats.eni_st_drv.drv_xm_nobuf++;
656 #ifdef  DO_LOG
657                 log ( LOG_ERR,
658                         "eni_output: not enough room in buffer\n" );
659 #endif
660                 eup->eu_pif.pif_oerrors++;
661                 KB_FREEALL ( m );
662                 (void) splx(s);
663                 return;
664         }
665
666         /*
667          * Find out where current DMA pointers are at
668          */
669         dma_start = dma_wr = eup->eu_midway[MIDWAY_TX_WR];
670         dma_rd = eup->eu_midway[MIDWAY_TX_RD];
671
672         /*
673          * Figure out how much DMA room we have available
674          */
675         if ( dma_rd == dma_wr ) {               /* Queue is empty */
676                 dma_avail = DMA_LIST_SIZE;
677         } else {
678                 dma_avail = ( dma_rd + DMA_LIST_SIZE - dma_wr )
679                     & ( DMA_LIST_SIZE - 1 );
680         }
681         /*
682          * Check to see if we can describe this PDU or if we're:
683          * out of room, will wrap past recovered resources.
684          */
685         if ( dma_avail < (j / 2 + 4) ||
686             ( dma_wr + (j / 2 + 4) > eup->eu_txdmawr + DMA_LIST_SIZE ) ) {
687                 /*
688                  * No space to insert DMA list into queue. Drop this PDU.
689                  */
690                 eup->eu_stats.eni_st_drv.drv_xm_nodma++;
691 #ifdef  DO_LOG
692                 log ( LOG_ERR,
693                         "eni_output: not enough room in DMA queue\n" );
694 #endif
695                 eup->eu_pif.pif_oerrors++;
696                 KB_FREEALL( m );
697                 (void) splx(s);
698                 return;
699         }
700
701         /*
702          * Create DMA descriptor for header. There is a descriptor word
703          * and also a cell header word which we'll set manually.
704          */
705         dma[0] = (((int)(eup->eu_txpos + 2) & (eup->eu_txsize-1)) <<
706             DMA_COUNT_SHIFT) | DMA_JK;
707         dma[1] = 0;
708
709         /*
710          * JK for AAL5 trailer. Set END bit as well.
711          */
712         if ( aal5 ) {
713             dma[j++] = (((int)(eup->eu_txpos+size) & (eup->eu_txsize-1)) <<
714                 DMA_COUNT_SHIFT) | DMA_END_BIT | DMA_JK;
715             dma[j++] = 0;
716         } else {
717                 dma[j-2] |= DMA_END_BIT;        /* Backup and set END bit */
718         }
719
720         /*
721          * Find out where in adapter memory this TX buffer starts.
722          */
723         tx_send = (Eni_mem)
724             ((((int)eup->eu_midway[MIDWAY_TXPLACE] & 0x7ff) << ENI_LOC_PREDIV) +
725                     (int)eup->eu_ram);
726
727         /*
728          * Set descriptor word
729          */
730         tx_send[eup->eu_txpos] =
731                 (MIDWAY_UNQ_ID << 28) | (aal5 ? 1 << 27 : 0)
732                         | (size / WORDS_PER_CELL);
733         /*
734          * Set cell header
735          */
736         tx_send[(eup->eu_txpos+1)&(eup->eu_txsize-1)] = 
737                 evp->ev_connvc->cvc_vcc->vc_vci << 4;
738
739         /*
740          * We've got all our resources, count the stats
741          */
742         if ( aal5 ) {
743                 /*
744                  * If this is an AAL5 PDU, we need to set the length
745                  */
746                 tx_send[(eup->eu_txpos+size-2) &
747                         (eup->eu_txsize-1)] = pdulen;
748                 /*
749                  * Increment AAL5 stats
750                  */
751                 eup->eu_stats.eni_st_aal5.aal5_pdu_xmit++;
752                 eup->eu_stats.eni_st_aal5.aal5_xmit += (size - 2) / WORDS_PER_CELL;
753         } else {
754                 /*
755                  * Increment AAL0 stats
756                  */
757                 eup->eu_stats.eni_st_aal0.aal0_xmit += (size - 2) / WORDS_PER_CELL;
758         }
759         /*
760          * Increment ATM stats
761          */
762         eup->eu_stats.eni_st_atm.atm_xmit += (size - 2) / WORDS_PER_CELL;
763
764         /*
765          * Store the DMA list
766          */
767         j = j >> 1;
768         for ( i = 0; i < j; i++ ) {
769                 eup->eu_txdma[dma_wr*2] = dma[i*2];
770                 eup->eu_txdma[dma_wr*2+1] = dma[i*2+1];
771                 dma_wr = (dma_wr+1) & (DMA_LIST_SIZE-1);
772         }
773
774         /*
775          * Build drain buffer
776          *
777          * We toss four words in to help keep track of this
778          * PDU. The first is a pointer to the VC control block
779          * so we can find which VCI this went out on, the second
780          * is the start and stop pointers for the DMA list which
781          * describes this PDU, the third is the PDU length
782          * since we'll want to know that for stats gathering,
783          * and the fourth is the number of DMA words.
784          */
785         KB_DATASTART ( m, up, u_long * );
786         *up++ = (u_long)cvp;
787         *up++ = dma_start << 16 | dma_wr;
788         *up++ = pdulen;
789         *up = size;
790
791         /*
792          * Set length of our buffer
793          */
794         KB_LEN ( m ) = 4 * sizeof ( long );
795
796         /*
797          * Place buffers onto transmit queue for draining
798          */
799         s2 = splimp();
800         IF_ENQUEUE ( &eup->eu_txqueue, m );
801         (void) splx(s2);
802
803         /*
804          * Update next word to be stored
805          */
806         eup->eu_txpos = ((eup->eu_txpos + size) & (eup->eu_txsize - 1));
807
808         /*
809          * Update MIDWAY_TX_WR pointer
810          */
811         eup->eu_midway[MIDWAY_TX_WR] = dma_wr;
812         
813         (void) splx ( s );
814
815         return;
816 }
817