aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/hfa/fore_receive.c
diff options
context:
space:
mode:
authorPoul-Henning Kamp <phk@FreeBSD.org>1998-09-15 08:23:17 +0000
committerPoul-Henning Kamp <phk@FreeBSD.org>1998-09-15 08:23:17 +0000
commit1820df7a2d615975e02b646f749fb05757e92f79 (patch)
treea7cd0079858aee749b0cbaadb313a655dee7765b /sys/dev/hfa/fore_receive.c
parent06915ea69a48dd223d1612cefc20795c2d38d359 (diff)
Notes
Diffstat (limited to 'sys/dev/hfa/fore_receive.c')
-rw-r--r--sys/dev/hfa/fore_receive.c582
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;
+}
+