diff options
Diffstat (limited to 'sys/dev/hea/eni_transmit.c')
-rw-r--r-- | sys/dev/hea/eni_transmit.c | 846 |
1 files changed, 0 insertions, 846 deletions
diff --git a/sys/dev/hea/eni_transmit.c b/sys/dev/hea/eni_transmit.c deleted file mode 100644 index 0335d71..0000000 --- a/sys/dev/hea/eni_transmit.c +++ /dev/null @@ -1,846 +0,0 @@ -/* - * - * =================================== - * HARP | Host ATM Research Platform - * =================================== - * - * - * This Host ATM Research Platform ("HARP") file (the "Software") is - * made available by Network Computing Services, Inc. ("NetworkCS") - * "AS IS". NetworkCS does not provide maintenance, improvements or - * support of any kind. - * - * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED, - * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE - * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE. - * In no event shall NetworkCS be responsible for any damages, including - * but not limited to consequential damages, arising from or relating to - * any use of the Software or related support. - * - * Copyright 1994-1998 Network Computing Services, Inc. - * - * Copies of this Software may be made, however, the above copyright - * notice must be reproduced on all copies. - * - * - */ - -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -/* - * Efficient ENI Adapter Support - * ----------------------------- - * - * Transmit queue management and PDU output processing - * - */ - - -#include <sys/param.h> -#include <sys/systm.h> -#include <sys/types.h> -#include <sys/time.h> -#include <sys/socket.h> -#include <sys/socketvar.h> -#include <vm/vm.h> -#include <vm/pmap.h> -#include <net/if.h> -#include <netinet/in.h> -#include <netatm/port.h> -#include <netatm/queue.h> -#include <netatm/atm.h> -#include <netatm/atm_sys.h> -#include <netatm/atm_sap.h> -#include <netatm/atm_cm.h> -#include <netatm/atm_if.h> -#include <netatm/atm_vc.h> -#include <netatm/atm_stack.h> -#include <netatm/atm_pcb.h> -#include <netatm/atm_var.h> - -#include <dev/hea/eni_stats.h> -#include <dev/hea/eni.h> -#include <dev/hea/eni_var.h> - -#ifndef lint -__RCSID("@(#) $FreeBSD$"); -#endif - -/* - * Make a variable which controls printing of PDUs - * as they travel through the driver. - */ -#ifdef DIAGNOSTIC -int eni_pdu_print = 0; -#endif - -/* - * Some PCI chipsets do not handle one or more of the 8WORD or - * 4WORD DMA transfer sizes. Default to using only 1WORD transfer - * sizes unless the user wishes to experiment. - * - * Make sure that these have to be changed here in this module. - */ -#define DMA_USE_8WORD -#define DMA_USE_4WORD - -/* - * Create a DMA list entry - * - * DMA entries consist of a control word and a physical address. - * Control words are comprised of a DMA type, a count of type transfers - * to occur, and a variable which for TX requests is the TX channel - * number and for RX requests is the VCC number. - * - * Arguments: - * eup pointer to unit structure - * rx set if receiving - * dma_list pointer to DMA list structure - * list_size length of DMA list structure - * idx pointer to current list entry - * val TX channel or RX vcc - * addr virtual DMA address of data buffer - * size size in bytes of DMA request to be built - * - * Returns: - * dma_list updated with new entries - * idx points to next list entry - * -1 no room in DMA list structure or DMA_GET_ADDR failed - */ -int -eni_set_dma ( eup, rx, dma_list, list_size, idx, val, addr, size ) -Eni_unit *eup; -int rx; -u_long *dma_list; -int list_size; -long *idx; -int val; -u_long addr; -int size; -{ - int dsize; /* Size of current DMA request */ - - /* - * Round up to multiple of word and convert to number - * of words rather then number of bytes. - */ - size = ( size + 3 ) >> 2; - -#ifdef DMA_USE_8WORD - /* - * Check for room in DMA list - we need two entires - */ - if ( *idx + 2 >= list_size ) - return ( -1 ); - - /* - * Here is the big win. Move as much data possible with - * n 8WORD DMAs. - */ - /* - * Check if we can do one or more 8WORD DMAs - */ - dsize = size & ~7; - if ( dsize ) { - dma_list[(*idx)++] = ( dsize >> 3 ) << DMA_COUNT_SHIFT | - val << DMA_VCC_SHIFT | DMA_8WORD; - dma_list[*idx] = (u_long)vtophys(addr); - if ( dma_list[*idx] == 0 ) { - if ( rx ) - eup->eu_stats.eni_st_drv.drv_rv_segdma++; - else - eup->eu_stats.eni_st_drv.drv_xm_segdma++; - return ( -1 ); /* DMA_GET_ADDR failed */ - } else - (*idx)++; /* increment index */ - /* - * Adjust addr and size - */ - addr += dsize << 2; - size &= 7; - } -#endif /* DMA_USE_8WORD */ - -#ifdef DMA_USE_4WORD - /* - * Check for room in DMA list - we need two entries - */ - if ( *idx + 2 >= list_size ) - return ( -1 ); - - /* - * Kindof a tossup from this point on. Since we hacked as many - * 8WORD DMAs off as possible, we are left with 0-7 words - * of remaining data. We could do upto one 4WORD with 0-3 - * words left, or upto three 2WORDS with 0-1 words left, - * or upto seven WORDS with nothing left. Someday we should - * experiment with performance and see if any particular - * combination is a better win then some other... - */ - /* - * Check if we can do one or more 4WORD DMAs - */ - dsize = size & ~3; - if ( dsize ) { - dma_list[(*idx)++] = ( dsize >> 2 ) << DMA_COUNT_SHIFT | - val << DMA_VCC_SHIFT | DMA_4WORD; - dma_list[*idx] = (u_long)vtophys(addr); - if ( dma_list[*idx] == 0 ) { - if ( rx ) - eup->eu_stats.eni_st_drv.drv_rv_segdma++; - else - eup->eu_stats.eni_st_drv.drv_xm_segdma++; - return ( -1 ); /* DMA_GET_ADDR failed */ - } else - (*idx)++; /* increment index */ - /* - * Adjust addr and size - */ - addr += dsize << 2; - size &= 3; - } -#endif /* DMA_USE_4WORD */ - - /* - * Check for room in DMA list - we need two entries - */ - if ( *idx + 2 >= list_size ) - return ( -1 ); - - /* - * Hard to know if one 2WORD and 0/1 WORD DMA would be better - * then 2/3 WORD DMAs. For now, skip 2WORD DMAs in favor of - * WORD DMAs. - */ - - /* - * Finish remaining size a 1WORD DMAs - */ - if ( size ) { - dma_list[(*idx)++] = ( size ) << DMA_COUNT_SHIFT | - val << DMA_VCC_SHIFT | DMA_WORD; - dma_list[*idx] = (u_long)vtophys(addr); - if ( dma_list[*idx] == 0 ) { - if ( rx ) - eup->eu_stats.eni_st_drv.drv_rv_segdma++; - else - eup->eu_stats.eni_st_drv.drv_xm_segdma++; - return ( -1 ); /* DMA_GET_ADDR failed */ - } else - (*idx)++; /* increment index */ - } - - /* - * Inserted descriptor okay - */ - return 0; -} - -/* - * Drain Transmit queue - * - * As PDUs are given to the adapter to be transmitted, we - * place them into a private ifqueue so that we can free - * any resources AFTER we know they've been successfully DMAed. - * As part of the output processing, we record the PDUs start - * and stop entries in the DMA list, and prevent wrapping. When - * we pull the top element off, we simply check that the current - * DMA location is outside this PDU and if so, it's okay to free - * things. - * - * PDUs are always in ascending order in the queue. - * - * Arguments: - * eup pointer to device unit structure - * - * Returns: - * none - * - */ -void -eni_xmit_drain ( eup ) - Eni_unit *eup; -{ - KBuffer *m; - Eni_vcc *evp; - struct vccb *vcp; - u_long pdulen; - u_long start, stop; - u_long dmap; - int s = splimp(); - - /* - * Pull the top element (PDU) off - */ - _IF_DEQUEUE ( &eup->eu_txqueue, m ); - /* - * As long as there are valid elements - */ - while ( m ) { - u_long *up; - - /* - * Find start of buffer - */ - KB_DATASTART ( m, up, u_long * ); - - /* - * First word is the VCC for this PDU - */ - /* - * NOTE: There is a potential problem here in that - * if the VCC is closed after this buffer was transmitted - * but before we get here, that while evp is non-null, - * it will not reference a valid vccb. We need to either - * delay closing the VCC until all references are removed - * from the drain stacks, actually go through the drain - * stacks and remove any references, or find someway of - * indicating that this vccb is nolonger usable. - */ - evp = (Eni_vcc *)*up++; - /* - * Second word is the start and stop DMA pointers - */ - start = *up >> 16; - stop = *up++ & 0xffff; - /* - * Find out where the TX engine is at - */ - dmap = eup->eu_midway[MIDWAY_TX_RD]; - /* - * Check to see if TX engine has processed this - * PDU yet. Remember that everything is circular - * and that stop might be less than start numerically. - */ - if ( start > stop ) { - if ( !(dmap >= stop && dmap < start) ) { - /* - * Haven't finished this PDU yet - replace - * it as the head of list. - */ - _IF_PREPEND ( &eup->eu_txqueue, m ); - /* - * If this one isn't done, none of the others - * are either. - */ - (void) splx(s); - return; - } - } else { - if ( dmap < stop && dmap >= start ) { - /* - * Haven't finished this PDU yet - replace - * it as the head of list. - */ - _IF_PREPEND ( &eup->eu_txqueue, m ); - /* - * If this one isn't done, none of the others - * are either. - */ - (void) splx(s); - return; - } - } - - /* - * Count the PDU stats for this interface - */ - eup->eu_pif.pif_opdus++; - /* - * Third word is PDU length from eni_output(). - */ - pdulen = *up++; - eup->eu_txfirst = (eup->eu_txfirst + *up) & - (eup->eu_txsize - 1); - eup->eu_pif.pif_obytes += pdulen; - - /* - * Now lookup the VCC entry and counts the stats for - * this VC. - */ - if ( evp ) { - vcp = evp->ev_connvc->cvc_vcc; - if ( vcp ) { - vcp->vc_opdus++; - vcp->vc_obytes += pdulen; - /* - * If we also have a network interface, count the PDU - * there also. - */ - if ( vcp->vc_nif ) { - vcp->vc_nif->nif_obytes += pdulen; - vcp->vc_nif->nif_if.if_opackets++; -#if (defined(BSD) && (BSD >= 199103)) - vcp->vc_nif->nif_if.if_obytes += pdulen; -#endif - } - } - } - /* - * Free the buffer chain - */ - KB_FREEALL ( m ); - - /* - * Advance DMA write okay pointer - */ - eup->eu_txdmawr = stop; - - /* - * Look for next completed transmit PDU - */ - _IF_DEQUEUE ( &eup->eu_txqueue, m ); - } - /* - * We've drained the queue... - */ - (void) splx(s); - return; -} - -/* - * Output a PDU - * - * This function is called via the common driver code after receiving a - * stack *_DATA* command. The common code has already validated most of - * the request so we just need to check a few more ENI-specific details. - * Then we just build a segmentation structure for the PDU and place the - * address into the DMA_Transmit_queue. - * - * Arguments: - * cup pointer to device common unit - * cvp pointer to common VCC entry - * m pointer to output PDU buffer chain head - * - * Returns: - * none - * - */ -void -eni_output ( cup, cvp, m ) - Cmn_unit *cup; - Cmn_vcc *cvp; - KBuffer *m; -{ - Eni_unit *eup = (Eni_unit *)cup; - Eni_vcc *evp = (Eni_vcc *)cvp; - int s, s2; - int pdulen = 0; - u_long size; - u_long buf_avail; - u_long dma_rd, dma_wr; - u_long dma[TEMP_DMA_SIZE]; - int aal5, i; - long j; - u_long dma_avail; - u_long dma_start; - Eni_mem tx_send; - u_long *up; - KBuffer *m0 = m, *m1, *mprev = NULL; - caddr_t cp, bfr; - u_int len, align; - int compressed = 0; - -#ifdef DIAGNOSTIC - if ( eni_pdu_print ) - atm_dev_pdu_print ( cup, cvp, m, "eni output" ); -#endif - - /* - * Re-entry point for after buffer compression (if needed) - */ -retry: - - /* - * We can avoid traversing the buffer list twice by building - * the middle (minus header and trailer) dma list at the - * same time we massage address and size alignments. Since - * this list remains local until we determine we've enough - * room, we're not going to trash anything by not checking - * sizes, etc. yet. Skip first entry to be used later to skip - * descriptor word. - */ - j = 2; - /* - * Do data positioning for address and length alignment - */ - while ( m ) { - u_long buf_addr; /* For passing addr to eni_set_dma() */ - - /* - * Get rid of any zero length buffers - */ - if ( KB_LEN ( m ) == 0 ) { - if ( mprev ) { - KB_UNLINK ( m, mprev, m1 ); - } else { - KB_UNLINKHEAD ( m, m1 ); - m0 = m1; - } - m = m1; - continue; - } - /* - * Get start of data onto full-word alignment - */ - KB_DATASTART ( m, cp, caddr_t ); - if ((align = ((uintptr_t)cp) & (sizeof(u_long)-1)) != 0) { - /* - * Gotta slide the data up - */ - eup->eu_stats.eni_st_drv.drv_xm_segnoal++; - bfr = cp - align; - bcopy ( cp, bfr, KB_LEN ( m ) ); - KB_HEADMOVE ( m, -align ); - } else { - /* - * Data already aligned - */ - bfr = cp; - } - /* - * Now work on getting the data length correct - */ - len = KB_LEN ( m ); - while ( ( align = ( len & (sizeof(u_long)-1))) && - (m1 = KB_NEXT ( m ) ) ) { - - /* - * Have to move some data from following buffer(s) - * to word-fill this buffer - */ - u_int ncopy = MIN ( sizeof(u_long) - align, - KB_LEN ( m1 ) ); - - if ( ncopy ) { - /* - * Move data to current buffer - */ - caddr_t dest; - - eup->eu_stats.eni_st_drv.drv_xm_seglen++; - KB_DATASTART ( m1, cp, caddr_t ); - dest = bfr + len; - KB_HEADADJ ( m1, -ncopy ); - KB_TAILADJ ( m, ncopy ); - len += ncopy; - while ( ncopy-- ) { - *dest++ = *cp++; - } - } - - /* - * If we've drained the buffer, free it - */ - if ( KB_LEN ( m1 ) == 0 ) { - KBuffer *m2; - - KB_UNLINK ( m1, m, m2 ); - } - } - - /* - * Address and size are now aligned. Build dma list - * using TX channel 0. Also, round length up to a word - * size which should only effect the last buffer in the - * chain. This works because the PDU length is maintained - * separately and we're not really adjusting the buffer's - * idea of its length. - */ - KB_DATASTART ( m, buf_addr, u_long ); - if ( eni_set_dma ( eup, 0, dma, TEMP_DMA_SIZE, &j, 0, - buf_addr, KB_LEN ( m ) ) < 0 ) { - /* - * Failed to build DMA list. First, we'll try to - * compress the buffer chain into a smaller number - * of buffers. After compressing, we'll try to send - * the new buffer chain. If we still fail, then - * we'll drop the pdu. - */ - if ( compressed ) { -#ifdef DO_LOG - log ( LOG_ERR, - "eni_output: eni_set_dma failed\n" ); -#endif - eup->eu_pif.pif_oerrors++; - KB_FREEALL ( m0 ); - return; - } - - eup->eu_stats.eni_st_drv.drv_xm_maxpdu++; - - m = atm_dev_compress ( m0 ); - if ( m == NULL ) { -#ifdef DO_LOG - log ( LOG_ERR, - "eni_output: atm_dev_compress() failed\n" ); -#endif - eup->eu_pif.pif_oerrors++; - return; - } - - /* - * Reset to new head of buffer chain - */ - m0 = m; - - /* - * Indicate we've been through here - */ - compressed = 1; - - /* - * Retry to build the DMA descriptors for the newly - * compressed buffer chain - */ - goto retry; - - } - - /* - * Now count the length - */ - pdulen += KB_LEN ( m ); - - /* - * Bump counters and get ready for next buffer - */ - mprev = m; - m = KB_NEXT ( m ); - } - - /* - * Get a buffer to use in a private queue so that we can - * reclaim resources after the DMA has finished. - */ - KB_ALLOC ( m, ENI_SMALL_BSIZE, KB_F_NOWAIT, KB_T_DATA ); - if ( m ) { - /* - * Link the PDU onto our new head - */ - KB_NEXT ( m ) = m0; - } else { - /* - * Drop this PDU and let the sender try again. - */ - eup->eu_stats.eni_st_drv.drv_xm_norsc++; -#ifdef DO_LOG - log(LOG_ERR, "eni_output: Unable to allocate drain buffer.\n"); -#endif - eup->eu_pif.pif_oerrors++; - KB_FREEALL ( m0 ); - return; - } - - s = splnet(); - - /* - * Calculate size of buffer necessary to store PDU. If this - * is an AAL5 PDU, we'll need to know where to stuff the length - * value in the trailer. - */ - /* - * AAL5 PDUs need an extra two words for control/length and - * CRC. Check for AAL5 and add requirements here. - */ - if ((aal5 = (evp->ev_connvc->cvc_attr.aal.type == ATM_AAL5)) != 0) - size = pdulen + 2 * sizeof(long); - else - size = pdulen; - /* - * Pad to next complete cell boundary - */ - size += (BYTES_PER_CELL - 1); - size -= size % BYTES_PER_CELL; - /* - * Convert size to words and add 2 words overhead for every - * PDU (descriptor and cell header). - */ - size = (size >> 2) + 2; - - /* - * First, check to see if there's enough buffer space to - * store the PDU. We do this by checking to see if the size - * required crosses the eu_txfirst pointer. However, we don't - * want to exactly fill the buffer, because we won't be able to - * distinguish between a full and empty buffer. - */ - if ( eup->eu_txpos == eup->eu_txfirst ) - buf_avail = eup->eu_txsize; - else - if ( eup->eu_txpos > eup->eu_txfirst ) - buf_avail = eup->eu_txsize - ( eup->eu_txpos - eup->eu_txfirst ); - else - buf_avail = eup->eu_txfirst - eup->eu_txpos; - - if ( size >= buf_avail ) - { - /* - * No buffer space in the adapter to store this PDU. - * Drop PDU and return. - */ - eup->eu_stats.eni_st_drv.drv_xm_nobuf++; -#ifdef DO_LOG - log ( LOG_ERR, - "eni_output: not enough room in buffer\n" ); -#endif - eup->eu_pif.pif_oerrors++; - KB_FREEALL ( m ); - (void) splx(s); - return; - } - - /* - * Find out where current DMA pointers are at - */ - dma_start = dma_wr = eup->eu_midway[MIDWAY_TX_WR]; - dma_rd = eup->eu_midway[MIDWAY_TX_RD]; - - /* - * Figure out how much DMA room we have available - */ - if ( dma_rd == dma_wr ) { /* Queue is empty */ - dma_avail = DMA_LIST_SIZE; - } else { - dma_avail = ( dma_rd + DMA_LIST_SIZE - dma_wr ) - & ( DMA_LIST_SIZE - 1 ); - } - /* - * Check to see if we can describe this PDU or if we're: - * out of room, will wrap past recovered resources. - */ - if ( dma_avail < (j / 2 + 4) || - ( dma_wr + (j / 2 + 4) > eup->eu_txdmawr + DMA_LIST_SIZE ) ) { - /* - * No space to insert DMA list into queue. Drop this PDU. - */ - eup->eu_stats.eni_st_drv.drv_xm_nodma++; -#ifdef DO_LOG - log ( LOG_ERR, - "eni_output: not enough room in DMA queue\n" ); -#endif - eup->eu_pif.pif_oerrors++; - KB_FREEALL( m ); - (void) splx(s); - return; - } - - /* - * Create DMA descriptor for header. There is a descriptor word - * and also a cell header word which we'll set manually. - */ - dma[0] = (((int)(eup->eu_txpos + 2) & (eup->eu_txsize-1)) << - DMA_COUNT_SHIFT) | DMA_JK; - dma[1] = 0; - - /* - * JK for AAL5 trailer. Set END bit as well. - */ - if ( aal5 ) { - dma[j++] = (((int)(eup->eu_txpos+size) & (eup->eu_txsize-1)) << - DMA_COUNT_SHIFT) | DMA_END_BIT | DMA_JK; - dma[j++] = 0; - } else { - dma[j-2] |= DMA_END_BIT; /* Backup and set END bit */ - } - - /* - * Find out where in adapter memory this TX buffer starts. - */ - tx_send = (Eni_mem) - ((((int)eup->eu_midway[MIDWAY_TXPLACE] & 0x7ff) << ENI_LOC_PREDIV) + - (intptr_t)eup->eu_ram); - - /* - * Set descriptor word - */ - tx_send[eup->eu_txpos] = - (MIDWAY_UNQ_ID << 28) | (aal5 ? 1 << 27 : 0) - | (size / WORDS_PER_CELL); - /* - * Set cell header - */ - tx_send[(eup->eu_txpos+1)&(eup->eu_txsize-1)] = - evp->ev_connvc->cvc_vcc->vc_vci << 4; - - /* - * We've got all our resources, count the stats - */ - if ( aal5 ) { - /* - * If this is an AAL5 PDU, we need to set the length - */ - tx_send[(eup->eu_txpos+size-2) & - (eup->eu_txsize-1)] = pdulen; - /* - * Increment AAL5 stats - */ - eup->eu_stats.eni_st_aal5.aal5_pdu_xmit++; - eup->eu_stats.eni_st_aal5.aal5_xmit += (size - 2) / WORDS_PER_CELL; - } else { - /* - * Increment AAL0 stats - */ - eup->eu_stats.eni_st_aal0.aal0_xmit += (size - 2) / WORDS_PER_CELL; - } - /* - * Increment ATM stats - */ - eup->eu_stats.eni_st_atm.atm_xmit += (size - 2) / WORDS_PER_CELL; - - /* - * Store the DMA list - */ - j = j >> 1; - for ( i = 0; i < j; i++ ) { - eup->eu_txdma[dma_wr*2] = dma[i*2]; - eup->eu_txdma[dma_wr*2+1] = dma[i*2+1]; - dma_wr = (dma_wr+1) & (DMA_LIST_SIZE-1); - } - - /* - * Build drain buffer - * - * We toss four words in to help keep track of this - * PDU. The first is a pointer to the VC control block - * so we can find which VCI this went out on, the second - * is the start and stop pointers for the DMA list which - * describes this PDU, the third is the PDU length - * since we'll want to know that for stats gathering, - * and the fourth is the number of DMA words. - */ - KB_DATASTART ( m, up, u_long * ); - *up++ = (u_long)cvp; - *up++ = dma_start << 16 | dma_wr; - *up++ = pdulen; - *up = size; - - /* - * Set length of our buffer - */ - KB_LEN ( m ) = 4 * sizeof ( long ); - - /* - * Place buffers onto transmit queue for draining - */ - s2 = splimp(); - _IF_ENQUEUE ( &eup->eu_txqueue, m ); - (void) splx(s2); - - /* - * Update next word to be stored - */ - eup->eu_txpos = ((eup->eu_txpos + size) & (eup->eu_txsize - 1)); - - /* - * Update MIDWAY_TX_WR pointer - */ - eup->eu_midway[MIDWAY_TX_WR] = dma_wr; - - (void) splx ( s ); - - return; -} - |