3 * ===================================
4 * HARP | Host ATM Research Platform
5 * ===================================
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.
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.
21 * Copyright 1994-1998 Network Computing Services, Inc.
23 * Copies of this Software may be made, however, the above copyright
24 * notice must be reproduced on all copies.
26 * @(#) $FreeBSD: src/sys/dev/hea/eni_transmit.c,v 1.6 1999/12/21 08:24:35 eivind Exp $
31 * Efficient ENI Adapter Support
32 * -----------------------------
34 * Transmit queue management and PDU output processing
39 #include <netatm/kern_include.h>
41 #include <dev/hea/eni_stats.h>
42 #include <dev/hea/eni.h>
43 #include <dev/hea/eni_var.h>
46 __RCSID("@(#) $FreeBSD: src/sys/dev/hea/eni_transmit.c,v 1.6 1999/12/21 08:24:35 eivind Exp $");
50 * Make a variable which controls printing of PDUs
51 * as they travel through the driver.
54 int eni_pdu_print = 0;
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.
62 * Make sure that these have to be changed here in this module.
68 * Create a DMA list entry
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.
76 * eup pointer to unit structure
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
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
91 eni_set_dma ( eup, rx, dma_list, list_size, idx, val, addr, size )
100 int dsize; /* Size of current DMA request */
103 * Round up to multiple of word and convert to number
104 * of words rather then number of bytes.
106 size = ( size + 3 ) >> 2;
110 * Check for room in DMA list - we need two entires
112 if ( *idx + 2 >= list_size )
116 * Here is the big win. Move as much data possible with
120 * Check if we can do one or more 8WORD DMAs
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 ) {
129 eup->eu_stats.eni_st_drv.drv_rv_segdma++;
131 eup->eu_stats.eni_st_drv.drv_xm_segdma++;
132 return ( -1 ); /* DMA_GET_ADDR failed */
134 (*idx)++; /* increment index */
136 * Adjust addr and size
141 #endif /* DMA_USE_8WORD */
145 * Check for room in DMA list - we need two entries
147 if ( *idx + 2 >= list_size )
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...
160 * Check if we can do one or more 4WORD DMAs
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 ) {
169 eup->eu_stats.eni_st_drv.drv_rv_segdma++;
171 eup->eu_stats.eni_st_drv.drv_xm_segdma++;
172 return ( -1 ); /* DMA_GET_ADDR failed */
174 (*idx)++; /* increment index */
176 * Adjust addr and size
181 #endif /* DMA_USE_4WORD */
184 * Check for room in DMA list - we need two entries
186 if ( *idx + 2 >= list_size )
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
196 * Finish remaining size a 1WORD DMAs
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 ) {
204 eup->eu_stats.eni_st_drv.drv_rv_segdma++;
206 eup->eu_stats.eni_st_drv.drv_xm_segdma++;
207 return ( -1 ); /* DMA_GET_ADDR failed */
209 (*idx)++; /* increment index */
213 * Inserted descriptor okay
219 * Drain Transmit queue
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
230 * PDUs are always in ascending order in the queue.
233 * eup pointer to device unit structure
240 eni_xmit_drain ( eup )
252 * Pull the top element (PDU) off
254 IF_DEQUEUE ( &eup->eu_txqueue, m );
256 * As long as there are valid elements
262 * Find start of buffer
264 KB_DATASTART ( m, up, u_long * );
267 * First word is the VCC for this PDU
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.
279 evp = (Eni_vcc *)*up++;
281 * Second word is the start and stop DMA pointers
284 stop = *up++ & 0xffff;
286 * Find out where the TX engine is at
288 dmap = eup->eu_midway[MIDWAY_TX_RD];
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.
294 if ( start > stop ) {
295 if ( !(dmap >= stop && dmap < start) ) {
297 * Haven't finished this PDU yet - replace
298 * it as the head of list.
300 IF_PREPEND ( &eup->eu_txqueue, m );
302 * If this one isn't done, none of the others
309 if ( dmap < stop && dmap >= start ) {
311 * Haven't finished this PDU yet - replace
312 * it as the head of list.
314 IF_PREPEND ( &eup->eu_txqueue, m );
316 * If this one isn't done, none of the others
325 * Count the PDU stats for this interface
327 eup->eu_pif.pif_opdus++;
329 * Third word is PDU length from eni_output().
332 eup->eu_txfirst = (eup->eu_txfirst + *up) &
333 (eup->eu_txsize - 1);
334 eup->eu_pif.pif_obytes += pdulen;
337 * Now lookup the VCC entry and counts the stats for
341 vcp = evp->ev_connvc->cvc_vcc;
344 vcp->vc_obytes += pdulen;
346 * If we also have a network interface, count the PDU
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;
359 * Free the buffer chain
364 * Advance DMA write okay pointer
366 eup->eu_txdmawr = stop;
369 * Look for next completed transmit PDU
371 IF_DEQUEUE ( &eup->eu_txqueue, m );
374 * We've drained the queue...
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.
390 * cup pointer to device common unit
391 * cvp pointer to common VCC entry
392 * m pointer to output PDU buffer chain head
399 eni_output ( cup, cvp, m )
404 Eni_unit *eup = (Eni_unit *)cup;
405 Eni_vcc *evp = (Eni_vcc *)cvp;
410 u_long dma_rd, dma_wr;
411 u_long dma[TEMP_DMA_SIZE];
418 KBuffer *m0 = m, *m1, *mprev = NULL;
425 atm_dev_pdu_print ( cup, cvp, m, "eni output" );
429 * Re-entry point for after buffer compression (if needed)
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
444 * Do data positioning for address and length alignment
447 u_long buf_addr; /* For passing addr to eni_set_dma() */
450 * Get rid of any zero length buffers
452 if ( KB_LEN ( m ) == 0 ) {
454 KB_UNLINK ( m, mprev, m1 );
456 KB_UNLINKHEAD ( m, m1 );
463 * Get start of data onto full-word alignment
465 KB_DATASTART ( m, cp, caddr_t );
466 if ((align = ((u_int)cp) & (sizeof(u_long)-1)) != 0) {
468 * Gotta slide the data up
470 eup->eu_stats.eni_st_drv.drv_xm_segnoal++;
472 KM_COPY ( cp, bfr, KB_LEN ( m ) );
473 KB_HEADMOVE ( m, -align );
476 * Data already aligned
481 * Now work on getting the data length correct
484 while ( ( align = ( len & (sizeof(u_long)-1))) &&
485 (m1 = KB_NEXT ( m ) ) ) {
488 * Have to move some data from following buffer(s)
489 * to word-fill this buffer
491 u_int ncopy = MIN ( sizeof(u_long) - align,
496 * Move data to current buffer
500 eup->eu_stats.eni_st_drv.drv_xm_seglen++;
501 KB_DATASTART ( m1, cp, caddr_t );
503 KB_HEADADJ ( m1, -ncopy );
504 KB_TAILADJ ( m, ncopy );
512 * If we've drained the buffer, free it
514 if ( KB_LEN ( m1 ) == 0 ) {
517 KB_UNLINK ( m1, m, m2 );
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.
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 ) {
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.
542 "eni_output: eni_set_dma failed\n" );
544 eup->eu_pif.pif_oerrors++;
549 eup->eu_stats.eni_st_drv.drv_xm_maxpdu++;
551 m = atm_dev_compress ( m0 );
555 "eni_output: atm_dev_compress() failed\n" );
557 eup->eu_pif.pif_oerrors++;
562 * Reset to new head of buffer chain
567 * Indicate we've been through here
572 * Retry to build the DMA descriptors for the newly
573 * compressed buffer chain
580 * Now count the length
582 pdulen += KB_LEN ( m );
585 * Bump counters and get ready for next buffer
592 * Get a buffer to use in a private queue so that we can
593 * reclaim resources after the DMA has finished.
595 KB_ALLOC ( m, ENI_SMALL_BSIZE, KB_F_NOWAIT, KB_T_DATA );
598 * Link the PDU onto our new head
603 * Drop this PDU and let the sender try again.
605 eup->eu_stats.eni_st_drv.drv_xm_norsc++;
607 log(LOG_ERR, "eni_output: Unable to allocate drain buffer.\n");
609 eup->eu_pif.pif_oerrors++;
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.
622 * AAL5 PDUs need an extra two words for control/length and
623 * CRC. Check for AAL5 and add requirements here.
625 if ((aal5 = (evp->ev_connvc->cvc_attr.aal.type == ATM_AAL5)) != 0)
626 size = pdulen + 2 * sizeof(long);
630 * Pad to next complete cell boundary
632 size += (BYTES_PER_CELL - 1);
633 size -= size % BYTES_PER_CELL;
635 * Convert size to words and add 2 words overhead for every
636 * PDU (descriptor and cell header).
638 size = (size >> 2) + 2;
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.
647 if ( eup->eu_txpos == eup->eu_txfirst )
648 buf_avail = eup->eu_txsize;
650 if ( eup->eu_txpos > eup->eu_txfirst )
651 buf_avail = eup->eu_txsize - ( eup->eu_txpos - eup->eu_txfirst );
653 buf_avail = eup->eu_txfirst - eup->eu_txpos;
655 if ( size >= buf_avail )
658 * No buffer space in the adapter to store this PDU.
659 * Drop PDU and return.
661 eup->eu_stats.eni_st_drv.drv_xm_nobuf++;
664 "eni_output: not enough room in buffer\n" );
666 eup->eu_pif.pif_oerrors++;
673 * Find out where current DMA pointers are at
675 dma_start = dma_wr = eup->eu_midway[MIDWAY_TX_WR];
676 dma_rd = eup->eu_midway[MIDWAY_TX_RD];
679 * Figure out how much DMA room we have available
681 if ( dma_rd == dma_wr ) { /* Queue is empty */
682 dma_avail = DMA_LIST_SIZE;
684 dma_avail = ( dma_rd + DMA_LIST_SIZE - dma_wr )
685 & ( DMA_LIST_SIZE - 1 );
688 * Check to see if we can describe this PDU or if we're:
689 * out of room, will wrap past recovered resources.
691 if ( dma_avail < (j / 2 + 4) ||
692 ( dma_wr + (j / 2 + 4) > eup->eu_txdmawr + DMA_LIST_SIZE ) ) {
694 * No space to insert DMA list into queue. Drop this PDU.
696 eup->eu_stats.eni_st_drv.drv_xm_nodma++;
699 "eni_output: not enough room in DMA queue\n" );
701 eup->eu_pif.pif_oerrors++;
708 * Create DMA descriptor for header. There is a descriptor word
709 * and also a cell header word which we'll set manually.
711 dma[0] = (((int)(eup->eu_txpos + 2) & (eup->eu_txsize-1)) <<
712 DMA_COUNT_SHIFT) | DMA_JK;
716 * JK for AAL5 trailer. Set END bit as well.
719 dma[j++] = (((int)(eup->eu_txpos+size) & (eup->eu_txsize-1)) <<
720 DMA_COUNT_SHIFT) | DMA_END_BIT | DMA_JK;
723 dma[j-2] |= DMA_END_BIT; /* Backup and set END bit */
727 * Find out where in adapter memory this TX buffer starts.
730 ((((int)eup->eu_midway[MIDWAY_TXPLACE] & 0x7ff) << ENI_LOC_PREDIV) +
734 * Set descriptor word
736 tx_send[eup->eu_txpos] =
737 (MIDWAY_UNQ_ID << 28) | (aal5 ? 1 << 27 : 0)
738 | (size / WORDS_PER_CELL);
742 tx_send[(eup->eu_txpos+1)&(eup->eu_txsize-1)] =
743 evp->ev_connvc->cvc_vcc->vc_vci << 4;
746 * We've got all our resources, count the stats
750 * If this is an AAL5 PDU, we need to set the length
752 tx_send[(eup->eu_txpos+size-2) &
753 (eup->eu_txsize-1)] = pdulen;
755 * Increment AAL5 stats
757 eup->eu_stats.eni_st_aal5.aal5_pdu_xmit++;
758 eup->eu_stats.eni_st_aal5.aal5_xmit += (size - 2) / WORDS_PER_CELL;
761 * Increment AAL0 stats
763 eup->eu_stats.eni_st_aal0.aal0_xmit += (size - 2) / WORDS_PER_CELL;
766 * Increment ATM stats
768 eup->eu_stats.eni_st_atm.atm_xmit += (size - 2) / WORDS_PER_CELL;
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);
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.
791 KB_DATASTART ( m, up, u_long * );
793 *up++ = dma_start << 16 | dma_wr;
798 * Set length of our buffer
800 KB_LEN ( m ) = 4 * sizeof ( long );
803 * Place buffers onto transmit queue for draining
806 IF_ENQUEUE ( &eup->eu_txqueue, m );
810 * Update next word to be stored
812 eup->eu_txpos = ((eup->eu_txpos + size) & (eup->eu_txsize - 1));
815 * Update MIDWAY_TX_WR pointer
817 eup->eu_midway[MIDWAY_TX_WR] = dma_wr;