dc72fe319e995ca969757e75132774d379bc55dc
[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.5 2004/01/08 18:39:17 asmodai 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 #if (defined(BSD) && (BSD >= 199103))
349                                 vcp->vc_nif->nif_if.if_obytes += pdulen;
350 #endif
351                         }
352                     }
353                 }
354                 /*
355                  * Free the buffer chain
356                  */
357                 KB_FREEALL ( m );
358
359                 /*
360                  * Advance DMA write okay pointer
361                  */
362                 eup->eu_txdmawr = stop;
363
364                 /*
365                  * Look for next completed transmit PDU
366                  */
367                 IF_DEQUEUE ( &eup->eu_txqueue, m );
368         }
369         /*
370          * We've drained the queue...
371          */
372         (void) splx(s);
373         return;
374 }
375
376 /*
377  * Output a PDU
378  *
379  * This function is called via the common driver code after receiving a
380  * stack *_DATA* command. The common code has already validated most of
381  * the request so we just need to check a few more ENI-specific details.
382  * Then we just build a segmentation structure for the PDU and place the
383  * address into the DMA_Transmit_queue.
384  *
385  * Arguments:
386  *      cup             pointer to device common unit
387  *      cvp             pointer to common VCC entry
388  *      m               pointer to output PDU buffer chain head
389  *
390  * Returns:
391  *      none
392  *
393  */
394 void
395 eni_output ( cup, cvp, m )
396         Cmn_unit        *cup;
397         Cmn_vcc         *cvp;
398         KBuffer         *m;
399 {
400         Eni_unit        *eup = (Eni_unit *)cup;
401         Eni_vcc         *evp = (Eni_vcc *)cvp;
402         int             s, s2;
403         int             pdulen = 0;
404         u_long          size;
405         u_long          buf_avail;
406         u_long          dma_rd, dma_wr;
407         u_long          dma[TEMP_DMA_SIZE];
408         int             aal5, i;
409         long            j;
410         u_long          dma_avail;
411         u_long          dma_start;
412         Eni_mem         tx_send;
413         u_long          *up;
414         KBuffer         *m0 = m, *m1, *mprev = NULL;
415         caddr_t         cp, bfr;
416         u_int           len, align;
417         int             compressed = 0;
418
419 #ifdef  DIAGNOSTIC
420         if ( eni_pdu_print )
421                 atm_dev_pdu_print ( cup, cvp, m, "eni output" );
422 #endif
423
424         /*
425          * Re-entry point for after buffer compression (if needed)
426          */
427 retry:
428
429         /*
430          * We can avoid traversing the buffer list twice by building
431          * the middle (minus header and trailer) dma list at the
432          * same time we massage address and size alignments. Since
433          * this list remains local until we determine we've enough
434          * room, we're not going to trash anything by not checking
435          * sizes, etc. yet. Skip first entry to be used later to skip
436          * descriptor word.
437          */
438         j = 2;
439         /*
440          * Do data positioning for address and length alignment
441          */
442         while ( m ) {
443                 u_long  buf_addr;       /* For passing addr to eni_set_dma() */
444
445                 /*
446                  * Get rid of any zero length buffers
447                  */
448                 if ( KB_LEN ( m ) == 0 ) {
449                         if ( mprev ) {
450                                 KB_UNLINK ( m, mprev, m1 );
451                         } else {
452                                 KB_UNLINKHEAD ( m, m1 );
453                                 m0 = m1;
454                         }
455                         m = m1;
456                         continue;
457                 }
458                 /*
459                  * Get start of data onto full-word alignment
460                  */
461                 KB_DATASTART ( m, cp, caddr_t );
462                 if ((align = ((u_int)cp) & (sizeof(u_long)-1)) != 0) {
463                         /*
464                          * Gotta slide the data up
465                          */
466                         eup->eu_stats.eni_st_drv.drv_xm_segnoal++;
467                         bfr = cp - align;
468                         KM_COPY ( cp, bfr, KB_LEN ( m ) );
469                         KB_HEADMOVE ( m, -align );
470                 } else {
471                         /*
472                          * Data already aligned
473                          */
474                         bfr = cp;
475                 }
476                 /*
477                  * Now work on getting the data length correct
478                  */
479                 len = KB_LEN ( m );
480                 while ( ( align = ( len & (sizeof(u_long)-1))) &&
481                         (m1 = KB_NEXT ( m ) ) ) {
482
483                         /*
484                          * Have to move some data from following buffer(s)
485                          * to word-fill this buffer
486                          */
487                         u_int ncopy = MIN ( sizeof(u_long) - align,
488                                 KB_LEN ( m1 ) );
489
490                         if ( ncopy ) {
491                                 /*
492                                  * Move data to current buffer
493                                  */
494                                 caddr_t dest;
495
496                                 eup->eu_stats.eni_st_drv.drv_xm_seglen++;
497                                 KB_DATASTART ( m1, cp, caddr_t );
498                                 dest = bfr + len;
499                                 KB_HEADADJ ( m1, -ncopy );
500                                 KB_TAILADJ ( m, ncopy );
501                                 len += ncopy;
502                                 while ( ncopy-- ) {
503                                         *dest++ = *cp++;
504                                 }
505                         }
506
507                         /*
508                          * If we've drained the buffer, free it
509                          */
510                         if ( KB_LEN ( m1 ) == 0 ) {
511                                 KBuffer *m2;
512
513                                 KB_UNLINK ( m1, m, m2 );
514                         }
515                 }
516
517                 /*
518                  * Address and size are now aligned. Build dma list
519                  * using TX channel 0. Also, round length up to a word
520                  * size which should only effect the last buffer in the
521                  * chain. This works because the PDU length is maintained
522                  * separately and we're not really adjusting the buffer's
523                  * idea of its length.
524                  */
525                 KB_DATASTART ( m, buf_addr, u_long );
526                 if ( eni_set_dma ( eup, 0, dma, TEMP_DMA_SIZE, &j, 0,
527                     buf_addr, KB_LEN ( m ) ) < 0 ) {
528                         /*
529                          * Failed to build DMA list. First, we'll try to
530                          * compress the buffer chain into a smaller number
531                          * of buffers. After compressing, we'll try to send
532                          * the new buffer chain. If we still fail, then
533                          * we'll drop the pdu.
534                          */
535                         if ( compressed ) {
536 #ifdef  DO_LOG
537                                 log ( LOG_ERR,
538                                         "eni_output: eni_set_dma failed\n" );
539 #endif
540                                 eup->eu_pif.pif_oerrors++;
541                                 KB_FREEALL ( m0 );
542                                 return;
543                         }
544
545                         eup->eu_stats.eni_st_drv.drv_xm_maxpdu++;
546
547                         m = atm_dev_compress ( m0 );
548                         if ( m == NULL ) {
549 #ifdef  DO_LOG
550                                 log ( LOG_ERR,
551                                     "eni_output: atm_dev_compress() failed\n" );
552 #endif
553                                 eup->eu_pif.pif_oerrors++;
554                                 return;
555                         }
556
557                         /*
558                          * Reset to new head of buffer chain
559                          */
560                         m0 = m;
561
562                         /*
563                          * Indicate we've been through here
564                          */
565                         compressed = 1;
566
567                         /*
568                          * Retry to build the DMA descriptors for the newly
569                          *  compressed buffer chain
570                          */
571                         goto retry;
572
573                 }
574
575                 /*
576                  * Now count the length
577                  */
578                 pdulen += KB_LEN ( m );
579
580                 /*
581                  * Bump counters and get ready for next buffer
582                  */
583                 mprev = m;
584                 m = KB_NEXT ( m );
585         }
586
587         /*
588          * Get a buffer to use in a private queue so that we can
589          * reclaim resources after the DMA has finished.
590          */
591         KB_ALLOC ( m, ENI_SMALL_BSIZE, KB_F_NOWAIT, KB_T_DATA );
592         if ( m ) {
593                 /*
594                  * Link the PDU onto our new head
595                  */
596                 KB_NEXT ( m ) = m0;
597         } else {
598                 /*
599                  * Drop this PDU and let the sender try again.
600                  */
601                 eup->eu_stats.eni_st_drv.drv_xm_norsc++;
602 #ifdef  DO_LOG
603                 log(LOG_ERR, "eni_output: Unable to allocate drain buffer.\n");
604 #endif
605                 eup->eu_pif.pif_oerrors++;
606                 KB_FREEALL ( m0 );
607                 return;
608         }
609
610         s = splnet();
611
612         /*
613          * Calculate size of buffer necessary to store PDU. If this
614          * is an AAL5 PDU, we'll need to know where to stuff the length
615          * value in the trailer.
616          */
617         /*
618          * AAL5 PDUs need an extra two words for control/length and
619          * CRC. Check for AAL5 and add requirements here.
620          */
621         if ((aal5 = (evp->ev_connvc->cvc_attr.aal.type == ATM_AAL5)) != 0)
622                 size = pdulen + 2 * sizeof(long);
623         else
624                 size = pdulen;
625         /*
626          * Pad to next complete cell boundary
627          */
628         size += (BYTES_PER_CELL - 1);
629         size -= size % BYTES_PER_CELL;
630         /*
631          * Convert size to words and add 2 words overhead for every
632          * PDU (descriptor and cell header).
633          */
634         size = (size >> 2) + 2;
635
636         /*
637          * First, check to see if there's enough buffer space to
638          * store the PDU. We do this by checking to see if the size
639          * required crosses the eu_txfirst pointer.  However, we don't
640          * want to exactly fill the buffer, because we won't be able to
641          * distinguish between a full and empty buffer.
642          */
643         if ( eup->eu_txpos == eup->eu_txfirst )
644                 buf_avail = eup->eu_txsize;
645         else
646             if ( eup->eu_txpos > eup->eu_txfirst )
647                 buf_avail = eup->eu_txsize - ( eup->eu_txpos - eup->eu_txfirst );
648             else
649                 buf_avail = eup->eu_txfirst - eup->eu_txpos;
650
651         if ( size >= buf_avail )
652         {
653                 /*
654                  * No buffer space in the adapter to store this PDU.
655                  * Drop PDU and return.
656                  */
657                 eup->eu_stats.eni_st_drv.drv_xm_nobuf++;
658 #ifdef  DO_LOG
659                 log ( LOG_ERR,
660                         "eni_output: not enough room in buffer\n" );
661 #endif
662                 eup->eu_pif.pif_oerrors++;
663                 KB_FREEALL ( m );
664                 (void) splx(s);
665                 return;
666         }
667
668         /*
669          * Find out where current DMA pointers are at
670          */
671         dma_start = dma_wr = eup->eu_midway[MIDWAY_TX_WR];
672         dma_rd = eup->eu_midway[MIDWAY_TX_RD];
673
674         /*
675          * Figure out how much DMA room we have available
676          */
677         if ( dma_rd == dma_wr ) {               /* Queue is empty */
678                 dma_avail = DMA_LIST_SIZE;
679         } else {
680                 dma_avail = ( dma_rd + DMA_LIST_SIZE - dma_wr )
681                     & ( DMA_LIST_SIZE - 1 );
682         }
683         /*
684          * Check to see if we can describe this PDU or if we're:
685          * out of room, will wrap past recovered resources.
686          */
687         if ( dma_avail < (j / 2 + 4) ||
688             ( dma_wr + (j / 2 + 4) > eup->eu_txdmawr + DMA_LIST_SIZE ) ) {
689                 /*
690                  * No space to insert DMA list into queue. Drop this PDU.
691                  */
692                 eup->eu_stats.eni_st_drv.drv_xm_nodma++;
693 #ifdef  DO_LOG
694                 log ( LOG_ERR,
695                         "eni_output: not enough room in DMA queue\n" );
696 #endif
697                 eup->eu_pif.pif_oerrors++;
698                 KB_FREEALL( m );
699                 (void) splx(s);
700                 return;
701         }
702
703         /*
704          * Create DMA descriptor for header. There is a descriptor word
705          * and also a cell header word which we'll set manually.
706          */
707         dma[0] = (((int)(eup->eu_txpos + 2) & (eup->eu_txsize-1)) <<
708             DMA_COUNT_SHIFT) | DMA_JK;
709         dma[1] = 0;
710
711         /*
712          * JK for AAL5 trailer. Set END bit as well.
713          */
714         if ( aal5 ) {
715             dma[j++] = (((int)(eup->eu_txpos+size) & (eup->eu_txsize-1)) <<
716                 DMA_COUNT_SHIFT) | DMA_END_BIT | DMA_JK;
717             dma[j++] = 0;
718         } else {
719                 dma[j-2] |= DMA_END_BIT;        /* Backup and set END bit */
720         }
721
722         /*
723          * Find out where in adapter memory this TX buffer starts.
724          */
725         tx_send = (Eni_mem)
726             ((((int)eup->eu_midway[MIDWAY_TXPLACE] & 0x7ff) << ENI_LOC_PREDIV) +
727                     (int)eup->eu_ram);
728
729         /*
730          * Set descriptor word
731          */
732         tx_send[eup->eu_txpos] =
733                 (MIDWAY_UNQ_ID << 28) | (aal5 ? 1 << 27 : 0)
734                         | (size / WORDS_PER_CELL);
735         /*
736          * Set cell header
737          */
738         tx_send[(eup->eu_txpos+1)&(eup->eu_txsize-1)] = 
739                 evp->ev_connvc->cvc_vcc->vc_vci << 4;
740
741         /*
742          * We've got all our resources, count the stats
743          */
744         if ( aal5 ) {
745                 /*
746                  * If this is an AAL5 PDU, we need to set the length
747                  */
748                 tx_send[(eup->eu_txpos+size-2) &
749                         (eup->eu_txsize-1)] = pdulen;
750                 /*
751                  * Increment AAL5 stats
752                  */
753                 eup->eu_stats.eni_st_aal5.aal5_pdu_xmit++;
754                 eup->eu_stats.eni_st_aal5.aal5_xmit += (size - 2) / WORDS_PER_CELL;
755         } else {
756                 /*
757                  * Increment AAL0 stats
758                  */
759                 eup->eu_stats.eni_st_aal0.aal0_xmit += (size - 2) / WORDS_PER_CELL;
760         }
761         /*
762          * Increment ATM stats
763          */
764         eup->eu_stats.eni_st_atm.atm_xmit += (size - 2) / WORDS_PER_CELL;
765
766         /*
767          * Store the DMA list
768          */
769         j = j >> 1;
770         for ( i = 0; i < j; i++ ) {
771                 eup->eu_txdma[dma_wr*2] = dma[i*2];
772                 eup->eu_txdma[dma_wr*2+1] = dma[i*2+1];
773                 dma_wr = (dma_wr+1) & (DMA_LIST_SIZE-1);
774         }
775
776         /*
777          * Build drain buffer
778          *
779          * We toss four words in to help keep track of this
780          * PDU. The first is a pointer to the VC control block
781          * so we can find which VCI this went out on, the second
782          * is the start and stop pointers for the DMA list which
783          * describes this PDU, the third is the PDU length
784          * since we'll want to know that for stats gathering,
785          * and the fourth is the number of DMA words.
786          */
787         KB_DATASTART ( m, up, u_long * );
788         *up++ = (u_long)cvp;
789         *up++ = dma_start << 16 | dma_wr;
790         *up++ = pdulen;
791         *up = size;
792
793         /*
794          * Set length of our buffer
795          */
796         KB_LEN ( m ) = 4 * sizeof ( long );
797
798         /*
799          * Place buffers onto transmit queue for draining
800          */
801         s2 = splimp();
802         IF_ENQUEUE ( &eup->eu_txqueue, m );
803         (void) splx(s2);
804
805         /*
806          * Update next word to be stored
807          */
808         eup->eu_txpos = ((eup->eu_txpos + size) & (eup->eu_txsize - 1));
809
810         /*
811          * Update MIDWAY_TX_WR pointer
812          */
813         eup->eu_midway[MIDWAY_TX_WR] = dma_wr;
814         
815         (void) splx ( s );
816
817         return;
818 }
819