freebsd-dev/sys/dev/hfa/fore_transmit.c
Poul-Henning Kamp 1820df7a2d Add new files for HARP3
Host ATM Research Platform (HARP), Network Computing Services, Inc.
This software was developed with the support of the Defense Advanced
Research Projects Agency (DARPA).
1998-09-15 08:23:17 +00:00

372 lines
7.6 KiB
C

/*
*
* ===================================
* 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;
}