aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/hfa/fore_transmit.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/hfa/fore_transmit.c')
-rw-r--r--sys/dev/hfa/fore_transmit.c371
1 files changed, 371 insertions, 0 deletions
diff --git a/sys/dev/hfa/fore_transmit.c b/sys/dev/hfa/fore_transmit.c
new file mode 100644
index 000000000000..744e7751c756
--- /dev/null
+++ b/sys/dev/hfa/fore_transmit.c
@@ -0,0 +1,371 @@
+/*
+ *
+ * ===================================
+ * 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_transmit.c,v 1.8 1998/07/17 20:19:37 root Exp $
+ *
+ */
+
+/*
+ * FORE Systems 200-Series Adapter Support
+ * ---------------------------------------
+ *
+ * Transmit queue management
+ *
+ */
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: fore_transmit.c,v 1.8 1998/07/17 20:19:37 root Exp $";
+#endif
+
+#include <dev/hfa/fore_include.h>
+
+
+/*
+ * Allocate Transmit Queue Data Structures
+ *
+ * Arguments:
+ * fup pointer to device unit structure
+ *
+ * Returns:
+ * 0 allocations successful
+ * else allocation failed
+ */
+int
+fore_xmit_allocate(fup)
+ Fore_unit *fup;
+{
+ void *memp;
+ H_xmit_queue *hxp;
+ int i;
+
+ /*
+ * Allocate non-cacheable memory for transmit status words
+ */
+ memp = atm_dev_alloc(sizeof(Q_status) * XMIT_QUELEN,
+ QSTAT_ALIGN, ATM_DEV_NONCACHE);
+ if (memp == NULL) {
+ return (1);
+ }
+ fup->fu_xmit_stat = (Q_status *) memp;
+
+ memp = DMA_GET_ADDR(fup->fu_xmit_stat, sizeof(Q_status) * XMIT_QUELEN,
+ QSTAT_ALIGN, ATM_DEV_NONCACHE);
+ if (memp == NULL) {
+ return (1);
+ }
+ fup->fu_xmit_statd = (Q_status *) memp;
+
+ /*
+ * Allocate memory for transmit descriptors
+ *
+ * We will allocate the transmit descriptors individually rather than
+ * as a single memory block, which will often be larger than a memory
+ * page. On some systems (eg. FreeBSD) the physical addresses of
+ * adjacent virtual memory pages are not contiguous.
+ */
+ hxp = fup->fu_xmit_q;
+ for (i = 0; i < XMIT_QUELEN; i++, hxp++) {
+
+ /*
+ * Allocate a transmit descriptor for this queue entry
+ */
+ hxp->hxq_descr = atm_dev_alloc(sizeof(Xmit_descr),
+ XMIT_DESCR_ALIGN, 0);
+ if (hxp->hxq_descr == NULL) {
+ return (1);
+ }
+
+ hxp->hxq_descr_dma = DMA_GET_ADDR(hxp->hxq_descr,
+ sizeof(Xmit_descr), XMIT_DESCR_ALIGN, 0);
+ if (hxp->hxq_descr_dma == NULL) {
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
+
+/*
+ * Transmit Queue Initialization
+ *
+ * Allocate and initialize the host-resident transmit 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_xmit_initialize(fup)
+ Fore_unit *fup;
+{
+ Aali *aap = fup->fu_aali;
+ Xmit_queue *cqp;
+ H_xmit_queue *hxp;
+ Q_status *qsp;
+ Q_status *qsp_dma;
+ int i;
+
+ /*
+ * Point to CP-resident transmit queue
+ */
+ cqp = (Xmit_queue *)(fup->fu_ram + CP_READ(aap->aali_xmit_q));
+
+ /*
+ * Point to host-resident transmit queue structures
+ */
+ hxp = fup->fu_xmit_q;
+ qsp = fup->fu_xmit_stat;
+ qsp_dma = fup->fu_xmit_statd;
+
+ /*
+ * Loop thru all queue entries and do whatever needs doing
+ */
+ for (i = 0; i < XMIT_QUELEN; i++) {
+
+ /*
+ * Set queue status word to free
+ */
+ *qsp = QSTAT_FREE;
+
+ /*
+ * Set up host queue entry and link into ring
+ */
+ hxp->hxq_cpelem = cqp;
+ hxp->hxq_status = qsp;
+ if (i == (XMIT_QUELEN - 1))
+ hxp->hxq_next = fup->fu_xmit_q;
+ else
+ hxp->hxq_next = hxp + 1;
+
+ /*
+ * Now let the CP into the game
+ */
+ cqp->cq_status = (CP_dma) CP_WRITE(qsp_dma);
+
+ /*
+ * Bump all queue pointers
+ */
+ hxp++;
+ qsp++;
+ qsp_dma++;
+ cqp++;
+ }
+
+ /*
+ * Initialize queue pointers
+ */
+ fup->fu_xmit_head = fup->fu_xmit_tail = fup->fu_xmit_q;
+
+ return;
+}
+
+
+/*
+ * Drain Transmit Queue
+ *
+ * This function will free all completed entries at the head of the
+ * transmit queue. Freeing the entry includes releasing the transmit
+ * buffers (buffer chain) back to the kernel.
+ *
+ * May be called in interrupt state.
+ * Must be called with interrupts locked out.
+ *
+ * Arguments:
+ * fup pointer to device unit structure
+ *
+ * Returns:
+ * none
+ */
+void
+fore_xmit_drain(fup)
+ Fore_unit *fup;
+{
+ H_xmit_queue *hxp;
+ H_dma *sdmap;
+ Fore_vcc *fvp;
+ struct vccb *vcp;
+ KBuffer *m;
+
+ /*
+ * Process each completed entry
+ */
+ while (*fup->fu_xmit_head->hxq_status & QSTAT_COMPLETED) {
+
+ hxp = fup->fu_xmit_head;
+
+ /*
+ * Release the entry's DMA addresses and buffer chain
+ */
+ for (m = hxp->hxq_buf, sdmap = hxp->hxq_dma; m;
+ m = KB_NEXT(m), sdmap++) {
+ caddr_t cp;
+
+ KB_DATASTART(m, cp, caddr_t);
+ DMA_FREE_ADDR(cp, *sdmap, KB_LEN(m), 0);
+ }
+ KB_FREEALL(hxp->hxq_buf);
+
+ /*
+ * Get VCC over which data was sent (may be null if
+ * VCC has been closed in the meantime)
+ */
+ fvp = hxp->hxq_vcc;
+
+ /*
+ * Now collect some statistics
+ */
+ if (*hxp->hxq_status & QSTAT_ERROR) {
+ /*
+ * CP ran into problems, not much we can do
+ * other than record the event
+ */
+ fup->fu_pif.pif_oerrors++;
+ if (fvp) {
+ vcp = fvp->fv_connvc->cvc_vcc;
+ vcp->vc_oerrors++;
+ if (vcp->vc_nif)
+ vcp->vc_nif->nif_if.if_oerrors++;
+ }
+ } else {
+ /*
+ * Good transmission
+ */
+ int len = XDS_GET_LEN(hxp->hxq_descr->xd_spec);
+
+ fup->fu_pif.pif_opdus++;
+ fup->fu_pif.pif_obytes += len;
+ if (fvp) {
+ vcp = fvp->fv_connvc->cvc_vcc;
+ vcp->vc_opdus++;
+ vcp->vc_obytes += len;
+ if (vcp->vc_nif) {
+ vcp->vc_nif->nif_obytes += len;
+ vcp->vc_nif->nif_if.if_opackets++;
+#if (defined(BSD) && (BSD >= 199103))
+ vcp->vc_nif->nif_if.if_obytes += len;
+#endif
+ }
+ }
+ }
+
+ /*
+ * Mark this entry free for use and bump head pointer
+ * to the next entry in the queue
+ */
+ *hxp->hxq_status = QSTAT_FREE;
+ fup->fu_xmit_head = hxp->hxq_next;
+ }
+
+ return;
+}
+
+
+/*
+ * Free Transmit Queue Data Structures
+ *
+ * Arguments:
+ * fup pointer to device unit structure
+ *
+ * Returns:
+ * none
+ */
+void
+fore_xmit_free(fup)
+ Fore_unit *fup;
+{
+ H_xmit_queue *hxp;
+ H_dma *sdmap;
+ KBuffer *m;
+ int i;
+
+ /*
+ * Free any transmit buffers left on the queue
+ */
+ if (fup->fu_flags & CUF_INITED) {
+ while (*fup->fu_xmit_head->hxq_status != QSTAT_FREE) {
+
+ hxp = fup->fu_xmit_head;
+
+ /*
+ * Release the entry's DMA addresses and buffer chain
+ */
+ for (m = hxp->hxq_buf, sdmap = hxp->hxq_dma; m;
+ m = KB_NEXT(m), sdmap++) {
+ caddr_t cp;
+
+ KB_DATASTART(m, cp, caddr_t);
+ DMA_FREE_ADDR(cp, *sdmap, KB_LEN(m), 0);
+ }
+ KB_FREEALL(hxp->hxq_buf);
+
+ *hxp->hxq_status = QSTAT_FREE;
+ fup->fu_xmit_head = hxp->hxq_next;
+ }
+ }
+
+ /*
+ * Free the status words
+ */
+ if (fup->fu_xmit_stat) {
+ if (fup->fu_xmit_statd) {
+ DMA_FREE_ADDR(fup->fu_xmit_stat, fup->fu_xmit_statd,
+ sizeof(Q_status) * XMIT_QUELEN,
+ ATM_DEV_NONCACHE);
+ }
+ atm_dev_free((void *)fup->fu_xmit_stat);
+ fup->fu_xmit_stat = NULL;
+ fup->fu_xmit_statd = NULL;
+ }
+
+ /*
+ * Free the transmit descriptors
+ */
+ hxp = fup->fu_xmit_q;
+ for (i = 0; i < XMIT_QUELEN; i++, hxp++) {
+
+ /*
+ * Free the transmit descriptor for this queue entry
+ */
+ if (hxp->hxq_descr_dma) {
+ DMA_FREE_ADDR(hxp->hxq_descr, hxp->hxq_descr_dma,
+ sizeof(Xmit_descr), 0);
+ hxp->hxq_descr_dma = NULL;
+ }
+
+ if (hxp->hxq_descr) {
+ atm_dev_free(hxp->hxq_descr);
+ hxp->hxq_descr = NULL;
+ }
+ }
+
+ return;
+}
+