freebsd-nq/sys/dev/hfa/fore_transmit.c
1999-08-28 01:08:13 +00:00

372 lines
7.5 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.
*
* @(#) $FreeBSD$
*
*/
/*
* FORE Systems 200-Series Adapter Support
* ---------------------------------------
*
* Transmit queue management
*
*/
#include <dev/hfa/fore_include.h>
#ifndef lint
__RCSID("@(#) $FreeBSD$");
#endif
/*
* 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((volatile 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;
}