📄 eni_transmit.c
字号:
/* * * =================================== * 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. * * @(#) $Id: eni_transmit.c,v 1.3 1998/10/31 20:06:45 phk Exp $ * *//* * Efficient ENI Adapter Support * ----------------------------- * * Transmit queue management and PDU output processing * */#include <netatm/kern_include.h>#include <dev/hea/eni_stats.h>#include <dev/hea/eni.h>#include <dev/hea/eni_var.h>#ifndef lint__RCSID("@(#) $Id: eni_transmit.c,v 1.3 1998/10/31 20:06:45 phk Exp $");#endif/* * Make a variable which controls printing of PDUs * as they travel through the driver. */#ifdef DIAGNOSTICint 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 */inteni_set_dma ( eup, rx, dma_list, list_size, idx, val, addr, size )Eni_unit *eup;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)DMA_GET_ADDR ( addr, dsize, 0, 0 ); if ( dma_list[*idx] == NULL ) { 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)DMA_GET_ADDR ( addr, dsize, 0, 0 ); if ( dma_list[*idx] == NULL ) { 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)DMA_GET_ADDR ( addr, size, 0, 0 ); if ( dma_list[*idx] == NULL ) { 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 * */voideni_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 * */voideni_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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -