fore_receive.c

来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 583 行

C
583
字号
/* * * =================================== * 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: fore_receive.c,v 1.3 1998/10/31 20:06:53 phk Exp $ * *//* * FORE Systems 200-Series Adapter Support * --------------------------------------- * * Receive queue management * */#include <dev/hfa/fore_include.h>#ifndef lint__RCSID("@(#) $Id: fore_receive.c,v 1.3 1998/10/31 20:06:53 phk Exp $");#endif/* * Local functions */static void	fore_recv_stack __P((void *, KBuffer *));/* * Allocate Receive Queue Data Structures * * Arguments: *	fup		pointer to device unit structure * * Returns: *	0		allocations successful *	else		allocation failed */intfore_recv_allocate(fup)	Fore_unit	*fup;{	caddr_t		memp;	/*	 * Allocate non-cacheable memory for receive status words	 */	memp = atm_dev_alloc(sizeof(Q_status) * RECV_QUELEN,			QSTAT_ALIGN, ATM_DEV_NONCACHE);	if (memp == NULL) {		return (1);	}	fup->fu_recv_stat = (Q_status *) memp;	memp = DMA_GET_ADDR(fup->fu_recv_stat, sizeof(Q_status) * RECV_QUELEN,			QSTAT_ALIGN, ATM_DEV_NONCACHE);	if (memp == NULL) {		return (1);	}	fup->fu_recv_statd = (Q_status *) memp;	/*	 * Allocate memory for receive descriptors	 */	memp = atm_dev_alloc(sizeof(Recv_descr) * RECV_QUELEN,			RECV_DESCR_ALIGN, 0);	if (memp == NULL) {		return (1);	}	fup->fu_recv_desc = (Recv_descr *) memp;	memp = DMA_GET_ADDR(fup->fu_recv_desc,			sizeof(Recv_descr) * RECV_QUELEN, RECV_DESCR_ALIGN, 0);	if (memp == NULL) {		return (1);	}	fup->fu_recv_descd = (Recv_descr *) memp;	return (0);}/* * Receive Queue Initialization * * Allocate and initialize the host-resident receive queue structures * and then initialize the CP-resident queue structures. *  * Called at interrupt level. * * Arguments: *	fup		pointer to device unit structure * * Returns: *	none */voidfore_recv_initialize(fup)	Fore_unit	*fup;{	Aali		*aap = fup->fu_aali;	Recv_queue	*cqp;	H_recv_queue	*hrp;	Recv_descr	*rdp;	Recv_descr	*rdp_dma;	Q_status	*qsp;	Q_status	*qsp_dma;	int		i;	/*	 * Point to CP-resident receive queue	 */	cqp = (Recv_queue *)(fup->fu_ram + CP_READ(aap->aali_recv_q));	/*	 * Point to host-resident receive queue structures	 */	hrp = fup->fu_recv_q;	qsp = fup->fu_recv_stat;	qsp_dma = fup->fu_recv_statd;	rdp = fup->fu_recv_desc;	rdp_dma = fup->fu_recv_descd;	/*	 * Loop thru all queue entries and do whatever needs doing	 */	for (i = 0; i < RECV_QUELEN; i++) {		/*		 * Set queue status word to free		 */		*qsp = QSTAT_FREE;		/*		 * Set up host queue entry and link into ring		 */		hrp->hrq_cpelem = cqp;		hrp->hrq_status = qsp;		hrp->hrq_descr = rdp;		hrp->hrq_descr_dma = rdp_dma;		if (i == (RECV_QUELEN - 1))			hrp->hrq_next = fup->fu_recv_q;		else			hrp->hrq_next = hrp + 1;		/*		 * Now let the CP into the game		 */		cqp->cq_descr = (CP_dma) CP_WRITE(rdp_dma);		cqp->cq_status = (CP_dma) CP_WRITE(qsp_dma);		/*		 * Bump all queue pointers		 */		hrp++;		qsp++;		qsp_dma++;		rdp++;		rdp_dma++;		cqp++;	}	/*	 * Initialize queue pointers	 */	fup->fu_recv_head = fup->fu_recv_q;	return;}/* * Drain Receive Queue * * This function will process all completed entries at the head of the * receive queue.  The received segments will be linked into a received * PDU buffer chain and it will then be passed up the PDU's VCC stack for  * processing by the next higher protocol layer. * * May be called in interrupt state. * Must be called with interrupts locked out. * * Arguments: *	fup		pointer to device unit structure * * Returns: *	none */voidfore_recv_drain(fup)	Fore_unit	*fup;{	H_recv_queue	*hrp = NULL;	Recv_descr	*rdp;	Recv_seg_descr	*rsp;	Buf_handle	*bhp;	Fore_vcc	*fvp;	struct vccb	*vcp;	KBuffer		*m, *mhead, *mtail;	caddr_t		cp;	u_long		hdr, nsegs;	u_int		seglen, type0;	int		i, pdulen, retries = 0, error;	/* Silence the compiler */	mtail = NULL;	type0 = 0;	/*	 * Process each completed entry	 */retry:	while (*fup->fu_recv_head->hrq_status & QSTAT_COMPLETED) {		/*		 * Get completed entry's receive descriptor		 */		hrp = fup->fu_recv_head;		rdp = hrp->hrq_descr;#ifdef VAC		/*		 * Cache flush receive descriptor 		 */		if (vac) {			vac_flush((addr_t)rdp, sizeof(Recv_descr));		}#endif		hdr = rdp->rd_cell_hdr;		nsegs = rdp->rd_nsegs;		pdulen = 0;		error = 0;		mhead = NULL;		/*		 * Locate incoming VCC for this PDU		 */		fvp = (Fore_vcc *) atm_dev_vcc_find((Cmn_unit *)fup,			ATM_HDR_GET_VPI(hdr), ATM_HDR_GET_VCI(hdr), VCC_IN);		/*		 * Check for a receive error		 *		 * Apparently the receive descriptor itself contains valid 		 * information, but the received pdu data is probably bogus.		 * We'll arrange for the receive buffer segments to be tossed.		 */		if (*hrp->hrq_status & QSTAT_ERROR) {			fup->fu_pif.pif_ierrors++;			if (fvp) {				vcp = fvp->fv_connvc->cvc_vcc;				vcp->vc_ierrors++;				if (vcp->vc_nif)					vcp->vc_nif->nif_if.if_ierrors++;			}			ATM_DEBUG1("fore receive error: hdr=0x%lx\n", hdr);			error = 1;		}		/*		 * Build PDU buffer chain from receive segments		 */		for (i = 0, rsp = rdp->rd_seg; i < nsegs; i++, rsp++) {			bhp = rsp->rsd_handle;			seglen = rsp->rsd_len;			/*			 * Remove buffer from our supplied queue and get			 * to the underlying buffer			 */			switch (bhp->bh_type) {			case BHT_S1_SMALL:				DEQUEUE(bhp, Buf_handle, bh_qelem,					fup->fu_buf1s_bq);				fup->fu_buf1s_cnt--;				m = (KBuffer *) ((caddr_t)bhp - BUF1_SM_HOFF);				KB_DATASTART(m, cp, caddr_t);				DMA_FREE_ADDR(cp, bhp->bh_dma, BUF1_SM_SIZE, 0);				break;			case BHT_S1_LARGE:				DEQUEUE(bhp, Buf_handle, bh_qelem,					fup->fu_buf1l_bq);				fup->fu_buf1l_cnt--;				m = (KBuffer *) ((caddr_t)bhp - BUF1_LG_HOFF);				KB_DATASTART(m, cp, caddr_t);				DMA_FREE_ADDR(cp, bhp->bh_dma, BUF1_LG_SIZE, 0);				break;			default:				log(LOG_ERR,					"fore_recv_drain: bhp=%p type=0x%x\n",					bhp, bhp->bh_type);				panic("fore_recv_drain: bad buffer type");			}			/*			 * Toss any zero-length or receive error buffers 			 */			if ((seglen == 0) || error) {				KB_FREEALL(m);				continue;			}			/*			 * Link buffer into chain			 */			if (mhead == NULL) {				type0 = bhp->bh_type;				KB_LINKHEAD(m, mhead);				mhead = m;			} else {				KB_LINK(m, mtail);			}			KB_LEN(m) = seglen;			pdulen += seglen;			mtail = m;			/*			 * Flush received buffer data			 */#ifdef VAC			if (vac) {				addr_t	dp;				KB_DATASTART(m, dp, addr_t);				vac_pageflush(dp);			}#endif		}		/*		 * Make sure we've got a non-null PDU		 */		if (mhead == NULL) {			goto free_ent;		}		/*		 * We only support user data PDUs (for now)		 */		if (hdr & ATM_HDR_SET_PT(ATM_PT_NONUSER)) {			KB_FREEALL(mhead);			goto free_ent;		}		/*		 * Toss the data if there's no VCC		 */		if (fvp == NULL) {			fup->fu_stats->st_drv.drv_rv_novcc++;			KB_FREEALL(mhead);			goto free_ent;		}#ifdef DIAGNOSTIC		if (atm_dev_print)			atm_dev_pdu_print((Cmn_unit *)fup, (Cmn_vcc *)fvp, 				mhead, "fore_recv");#endif		/*		 * Make sure we have our queueing headroom at the front		 * of the buffer chain		 */		if (type0 != BHT_S1_SMALL) {			/*			 * Small buffers already have headroom built-in, but			 * if CP had to use a large buffer for the first 			 * buffer, then we have to allocate a buffer here to			 * contain the headroom.			 */			fup->fu_stats->st_drv.drv_rv_nosbf++;			KB_ALLOCPKT(m, BUF1_SM_SIZE, KB_F_NOWAIT, KB_T_DATA);			if (m == NULL) {				fup->fu_stats->st_drv.drv_rv_nomb++;				KB_FREEALL(mhead);				goto free_ent;			}			/*			 * Put new buffer at head of PDU chain			 */			KB_LINKHEAD(m, mhead);			KB_LEN(m) = 0;			KB_HEADSET(m, BUF1_SM_DOFF);			mhead = m;		}		/*		 * It looks like we've got a valid PDU - count it quick!!		 */		KB_PLENSET(mhead, pdulen);		fup->fu_pif.pif_ipdus++;		fup->fu_pif.pif_ibytes += pdulen;		vcp = fvp->fv_connvc->cvc_vcc;		vcp->vc_ipdus++;		vcp->vc_ibytes += pdulen;		if (vcp->vc_nif) {			vcp->vc_nif->nif_ibytes += pdulen;			vcp->vc_nif->nif_if.if_ipackets++;#if (defined(BSD) && (BSD >= 199103))			vcp->vc_nif->nif_if.if_ibytes += pdulen;#endif		}		/*		 * The STACK_CALL needs to happen at splnet() in order		 * for the stack sequence processing to work.  Schedule an		 * interrupt queue callback at splnet() since we are 		 * currently at device level.		 */		/*		 * Prepend callback function pointer and token value to buffer.		 * We have already guaranteed that the space is available		 * in the first buffer.		 */		KB_HEADADJ(mhead, sizeof(atm_intr_func_t) + sizeof(int));		KB_DATASTART(mhead, cp, caddr_t);		*((atm_intr_func_t *)cp) = fore_recv_stack;		cp += sizeof(atm_intr_func_t);		*((void **)cp) = (void *)fvp;		/*		 * Schedule callback		 */		if (!IF_QFULL(&atm_intrq)) {			IF_ENQUEUE(&atm_intrq, mhead);			SCHED_ATM;		} else {			fup->fu_stats->st_drv.drv_rv_ifull++;			KB_FREEALL(mhead);			goto free_ent;		}free_ent:		/*		 * Mark this entry free for use and bump head pointer		 * to the next entry in the queue		 */		*hrp->hrq_status = QSTAT_FREE;		hrp->hrq_cpelem->cq_descr = 			(CP_dma) CP_WRITE((u_long)hrp->hrq_descr_dma);		fup->fu_recv_head = hrp->hrq_next;	}	/*	 * Nearly all of the interrupts generated by the CP will be due	 * to PDU reception.  However, we may receive an interrupt before	 * the CP has completed the status word DMA to host memory.  Thus,	 * if we haven't processed any PDUs during this interrupt, we will	 * wait a bit for completed work on the receive queue, rather than 	 * having to field an extra interrupt very soon.	 */	if (hrp == NULL) {		if (++retries <= FORE_RECV_RETRY) {			DELAY(FORE_RECV_DELAY);			goto retry;		}	}	return;}/* * Pass Incoming PDU up Stack * * This function is called via the core ATM interrupt queue callback  * set in fore_recv_drain().  It will pass the supplied incoming  * PDU up the incoming VCC's stack. * * Called at splnet. * * Arguments: *	tok		token to identify stack instantiation *	m		pointer to incoming PDU buffer chain * * Returns: *	none */static voidfore_recv_stack(tok, m)	void		*tok;	KBuffer		*m;{	Fore_vcc	*fvp = (Fore_vcc *)tok;	int		err;	/*	 * Send the data up the stack	 */	STACK_CALL(CPCS_UNITDATA_SIG, fvp->fv_upper,		fvp->fv_toku, fvp->fv_connvc, (int)m, 0, err);	if (err)		KB_FREEALL(m);	return;}/* * Free Receive Queue Data Structures * * Arguments: *	fup		pointer to device unit structure * * Returns: *	none */voidfore_recv_free(fup)	Fore_unit	*fup;{	/*	 * We'll just let fore_buf_free() take care of freeing any	 * buffers sitting on the receive queue (which are also still	 * on the fu_*_bq queue).	 */	if (fup->fu_flags & CUF_INITED) {	}	/*	 * Free the status words	 */	if (fup->fu_recv_stat) {		if (fup->fu_recv_statd) {			DMA_FREE_ADDR(fup->fu_recv_stat, fup->fu_recv_statd,				sizeof(Q_status) * RECV_QUELEN,				ATM_DEV_NONCACHE);		}		atm_dev_free((void *)fup->fu_recv_stat);		fup->fu_recv_stat = NULL;		fup->fu_recv_statd = NULL;	}	/*	 * Free the receive descriptors	 */	if (fup->fu_recv_desc) {		if (fup->fu_recv_descd) {			DMA_FREE_ADDR(fup->fu_recv_desc, fup->fu_recv_descd,				sizeof(Recv_descr) * RECV_QUELEN, 0);		}		atm_dev_free(fup->fu_recv_desc);		fup->fu_recv_desc = NULL;		fup->fu_recv_descd = NULL;	}	return;}

⌨️ 快捷键说明

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