⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 eni_transmit.c

📁 基于组件方式开发操作系统的OSKIT源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * * =================================== * 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 + -