eni_receive.c

来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 872 行 · 第 1/2 页

C
872
字号
/* * * =================================== * 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_receive.c,v 1.3 1998/10/31 20:06:45 phk Exp $ * *//* * Efficient ENI Adapter Support * ----------------------------- * * Receive management * */#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_receive.c,v 1.3 1998/10/31 20:06:45 phk Exp $");#endifstatic void	eni_recv_stack __P((void *, KBuffer *));#ifdef	DIAGNOSTICextern int	eni_pdu_print;#endif/* * Procedure to remove VCs from the Service List and generate DMA * requests to move the associated PDUs into host memory. As PDUs * are completed in adapter memory, the adapter examines the IN_SERVICE * bit for the VC in the VC table. If this bit is not set, the adapter * will place the VC number at the end of the service list queue, set * the IN_SERVICE bit in the VC table, and interrupt the host. The host * will remove VCs from the service list, clear the IN_SERVICE bit in * the VC table, and create a DMA list to move the PDU into host buffers. * * Arguments: *	eup		pointer to per unit structure * * Returns: *	none * */voideni_do_service ( eup )	Eni_unit *eup;{	int		vcc;	Eni_vcc		*evp;	u_long		servwrite;	VCI_Table	*vct;	u_long		rdptr;	u_long		*rxp;	KBuffer		*m;	u_long		dma[TEMP_DMA_SIZE];	u_long		i, j;	u_long		dma_rd, dma_wr;	u_long		dma_avail;	int		pdulen;	int		mask;	u_long		*upp;	/*	 * Where is the adapter currently inserting entries?	 */	servwrite = eup->eu_midway[MIDWAY_SVCWR] & SVC_SIZE_MASK;	/*	 * As long as we're not caught up with the adapter, keep	 * removing VCs from the service list.	 */	while ( servwrite != eup->eu_servread ) {		int	vci_hdr;		u_long	descr;		/*		 * Get VC number and find VC table entry.		 */		vcc = eup->eu_svclist[eup->eu_servread];		vct = &eup->eu_vcitbl[vcc];		vci_hdr = vct->vci_control;	/* Current status */		/*		 * Check that this VCC still needs servicing. We		 * might have closed this VCC down in between		 * the adapter setting the flag and our checking		 * the flag. Also check that we haven't placed the		 * VCC into TRASH mode.		 */		if ( ( vci_hdr & VCI_IN_SERVICE ) == 0 ||		    ( vci_hdr & ~VCI_MODE_MASK ==			VCI_MODE_TRASH << VCI_MODE_SHIFT ) )			    goto next_vcc;		/*		 * Find the size of this VCs buffer		 */		mask = (vci_hdr >> VCI_SIZE_SHIFT) & VCI_SIZE_MASK;		mask = 1 << (ENI_LOC_PREDIV + mask);		/* Turn byte count into word count */		mask >>= 2;		/*		 * Find the start of the adapter buffer for this VC.		 */		rxp = (u_long *)		    ((int)(((vci_hdr >> VCI_LOC_SHIFT ) & VCI_LOC_MASK)			<< ENI_LOC_PREDIV) + (int)eup->eu_ram);		/*		 * Locate incoming VCC for this PDU and find where we		 * should next read from.		 */		evp = (Eni_vcc *) atm_dev_vcc_find ( (Cmn_unit *)eup,		    0, vcc, VCC_IN );		if ( evp == (Eni_vcc *)NULL )			goto next_vcc;		/* VCI no longer active */		rdptr = evp->ev_rxpos;		/*		 * Find out where the adapter is currently reassembling.		 * The PDU which starts at descr is not yet complete so we		 * must stop there.		 */		descr = ( vct->vci_descr >> 16 ) & 0x7FFF;		/*		 * As long as we haven't processed all the completed PDUs on		 * this VC, keep going...		 */		while ( rdptr != descr )		{		    int		n_cells;		    int		pdu_descr;		    int		aal5;		    /*		     * Ensure that the following are reset for every new		     * PDU.		     */		    upp = NULL;		    m = NULL;		    /*		     * Fisrt build a DMA with JK to skip the descriptor word.		     * We must always skip the descriptor even if it turns out		     * that there isn't any PDU here.		     */		    j = 0;		    dma[j++] = (((rdptr + 1) & (mask-1)) << DMA_COUNT_SHIFT ) |		    	( vcc << DMA_VCC_SHIFT ) | DMA_JK;		    dma[j++] = 0;		    /*		     * We'll use some of the values below for skipping		     * bad PDUs or counting statistics so compute them		     * now.		     */		    /*		     * Grab a copy of the descriptor word		     */		    pdu_descr = rxp[rdptr];		    /*		     * Strip out cell count from descriptor word.		     * At this point, we still don't know if there		     * is any real data until after we check for		     * TRASH mode.		     */		    n_cells = pdu_descr & DESCR_CELL_COUNT;		    /*		     * Is this an AAL5 PDU? Check MODE in vci_hdr.		     */		    aal5 = ( ( vci_hdr & ~VCI_MODE_MASK ) ==			VCI_MODE_AAL5 << VCI_MODE_SHIFT );		    /*		     * Now check to see if we're trashing on this vcc.		     * If so, there is no data with this VC and the		     * next word after the current descriptor is the		     * descriptor for the next PDU.		     */		    if ( ( pdu_descr & DESCR_TRASH_BIT ) != 0 ) {			if ( aal5 )				/*				 * Count as number of AAL5 cells dropped				 */				eup->eu_stats.eni_st_aal5.aal5_drops += n_cells;			else				/*				 * Count as number of AAL0 cells dropped				 */				eup->eu_stats.eni_st_aal0.aal0_drops += n_cells;			eup->eu_pif.pif_ierrors++;			/*			 * When cells have been trashed, all we have in the			 * buffer is a descriptor word. There are no data			 * words. Set the number of cells to zero so that			 * we correctly skip to the next word which will			 * be the descriptor for the next PDU.			 */			n_cells = 0;			/*			 * Go issue the DMA to skip this descriptor word.			 */			goto send_dma;		    }		    /*		     * Data length: number of cells * cell size		     */		    pdulen = n_cells * BYTES_PER_CELL;		    /*		     * If this is an AAL5 PDU, then we need to check		     * for the presence of any CRC errors. If there		     * is one or more CRC errors, then we are going to		     * drop this PDU.		     */		    if ( aal5 && ( pdu_descr & DESCR_CRC_ERR ) ) {			/*			 * Count the stat			 */			eup->eu_pif.pif_ierrors++;			eup->eu_stats.eni_st_aal5.aal5_pdu_crc++;			if ( evp->ev_connvc->cvc_vcc )				evp->ev_connvc->cvc_vcc->vc_ierrors++;			/*			 * Build a DMA entry to skip the rest of this			 * PDU.			 */			dma[j++] =			    (((rdptr + n_cells*WORDS_PER_CELL + 1)				& (mask-1)) << DMA_COUNT_SHIFT ) |				    (vcc << DMA_VCC_SHIFT ) | DMA_JK;			dma[j++] = 0;			/*			 * All done with this PDU. Get a buffer to save some			 * data for reclamation services.			 */			KB_ALLOCPKT ( m, ENI_SMALL_BSIZE, KB_F_NOWAIT,			    KB_T_DATA );			if ( m ) {				u_long	*up;				KB_DATASTART ( m, up, u_long * );				/*				 * Indicate no PDU				 */				KB_PLENSET ( m, 0 );				/*				 * Set buffer length - only driver overhead				 */				KB_LEN ( m ) = 3 * sizeof ( u_long );				/*				 * Insert vcc, space for DMA pointers,				 * and pdulen				 */				*up++ = vcc;				upp = up;	/* Remember location */				up++;		/* And skip it */						/* - to be filled later */				*up = pdulen;	/* Actual PDU length if it */						/* were valid */			} else {				/*				 * We've a real problem here as now we can't				 * reclaim/advance resources/safety pointers.				 */				eup->eu_stats.eni_st_drv.drv_rv_norsc++;#ifdef	DO_LOG				log ( LOG_ERR,    "eni_do_service: No drain buffers available. Receiver about to lock.\n" );#endif			}			goto send_dma;		    }		    /*		     * Do we need to strip the AAL layer? Yes if this		     * is an AAL5 PDU.		     */		    if ( aal5 ) {			/*			 * Grab the CS-PDU length. Find the address of the			 * last word, back up one word to skip CRC, and			 * then mask the whole thing to handle circular wraps.			 */			pdulen = rxp[(rdptr + n_cells*WORDS_PER_CELL - 1)			    & (mask-1)]				& 0xFFFF;		    }		    /*		     * We now have a valid PDU of some length. Build		     * the necessary DMA list to move it into host		     * memory.		     */		    /*		     * Get an initial buffer.		     */		    KB_ALLOCPKT ( m, ENI_SMALL_BSIZE, KB_F_NOWAIT, KB_T_DATA );		    /*		     * Do we have a valid buffer?		     */		    if ( m != (KBuffer *)NULL )		    {			int	len;			u_long	*up;			KBuffer *m0;				KB_DATASTART ( m, up, u_long * );			/*			 * Fill in pdulen in PKTHDR structure (for IP).			 */			KB_PLENSET ( m, pdulen );			/*			 * We're going to save the VCI nuber, the start			 * and stop DMA pointers, and the PDU length at			 * the head of the buffer. We'll pull this out			 * later after the DMA has completed.			 *			 * Insert VCI number as first word in first buffer,			 * remeber where we want to store the start/stop			 * pointers, and store the PDU length.			 */			*up++ = vcc;	/* PDU's VCC */			upp = up;	/* Remember where we are */			up++;		/* To stuff start/stop pointers in */			*up++ = pdulen;	/* PDU's length */			/*			 * Leave some extra room in case a higher protocol			 * (IP) wants to do a pullup. Maybe we can keep			 * someone from having to allocate another buffer			 * a do a larger memory copy.			 */			len = MIN ( ENI_SMALL_BSIZE, pdulen );			(void) eni_set_dma ( eup, 1, dma, TEMP_DMA_SIZE, &j,				vcc, (u_long)up, len );			/*			 * Adjust length of remaining data in PDU			 */			pdulen -= len;			/*			 * Set buffer length, including our overhead			 */			KB_LEN ( m ) = len + 3 * sizeof ( u_long );			/*			 * Finish by moving anything which won't fit in			 * first buffer			 */			m0 = m;			while ( pdulen ) {				KBuffer *m1;				u_long	data_addr;					/*				 * Get another buffer				 */				KB_ALLOCEXT ( m1, ENI_LARGE_BSIZE, KB_F_NOWAIT,					KB_T_DATA );					/*				 * If we succeeded...				 */				if ( m1 ) {				    /*				     * Figure out how much we can move into				     * this buffer.				     */				    len = MIN ( ENI_LARGE_BSIZE, pdulen );				    /*				     * Setup DMA list for this buffer				     */				    KB_DATASTART ( m1, data_addr, u_long );				    (void) eni_set_dma					( eup, 1, dma, TEMP_DMA_SIZE, &j, vcc,					    data_addr, len );				    /*				     * Adjust remaining length				     */				    pdulen -= len;				    /*				     * Set buffer length				     */				    KB_LEN ( m1 ) = len;				    /*				     * Link new buffer onto end and advance				     * pointer				     */				    KB_NEXT ( m0 ) = m1;				    m0 = m1;				} else {				    /*				     * Either we were unable to grab another				     * buffer or there are no large buffers				     * available. We know that the first				     * buffer is valid, so drop everything				     * else, build a JK DMA to skip/drop this				     * PDU, set the pointers to reclaim				     * resources/advance pointers, and				     * finish this PDU now.				     */				    if ( KB_NEXT ( m ) )				    	KB_FREEALL ( KB_NEXT ( m ) );				    eup->eu_pif.pif_ierrors++;				    j = 2;				    dma[j++] =				        (((rdptr + n_cells*WORDS_PER_CELL + 1)					    & (mask-1)) << DMA_COUNT_SHIFT ) |					        (vcc << DMA_VCC_SHIFT ) |

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?