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