diff options
| author | Poul-Henning Kamp <phk@FreeBSD.org> | 1998-09-15 08:23:17 +0000 |
|---|---|---|
| committer | Poul-Henning Kamp <phk@FreeBSD.org> | 1998-09-15 08:23:17 +0000 |
| commit | 1820df7a2d615975e02b646f749fb05757e92f79 (patch) | |
| tree | a7cd0079858aee749b0cbaadb313a655dee7765b /sys/dev/hfa/fore_receive.c | |
| parent | 06915ea69a48dd223d1612cefc20795c2d38d359 (diff) | |
Notes
Diffstat (limited to 'sys/dev/hfa/fore_receive.c')
| -rw-r--r-- | sys/dev/hfa/fore_receive.c | 582 |
1 files changed, 582 insertions, 0 deletions
diff --git a/sys/dev/hfa/fore_receive.c b/sys/dev/hfa/fore_receive.c new file mode 100644 index 000000000000..f9a9d1961ed2 --- /dev/null +++ b/sys/dev/hfa/fore_receive.c @@ -0,0 +1,582 @@ +/* + * + * =================================== + * 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.10 1998/07/17 20:19:35 root Exp $ + * + */ + +/* + * FORE Systems 200-Series Adapter Support + * --------------------------------------- + * + * Receive queue management + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: fore_receive.c,v 1.10 1998/07/17 20:19:35 root Exp $"; +#endif + +#include <dev/hfa/fore_include.h> + + +/* + * 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 + */ +int +fore_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 + */ +void +fore_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 + */ +void +fore_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%x\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=0x%x type=0x%x\n", + (int)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 void +fore_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 + */ +void +fore_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; +} + |
