608 lines
13 KiB
C
608 lines
13 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
|
|
* ---------------------------------------
|
|
*
|
|
* Receive queue management
|
|
*
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/socketvar.h>
|
|
#include <sys/syslog.h>
|
|
#include <vm/vm.h>
|
|
#include <vm/pmap.h>
|
|
#include <net/if.h>
|
|
#include <net/netisr.h>
|
|
#include <netatm/port.h>
|
|
#include <netatm/queue.h>
|
|
#include <netatm/atm.h>
|
|
#include <netatm/atm_sys.h>
|
|
#include <netatm/atm_sap.h>
|
|
#include <netatm/atm_cm.h>
|
|
#include <netatm/atm_if.h>
|
|
#include <netatm/atm_vc.h>
|
|
#include <netatm/atm_stack.h>
|
|
#include <netatm/atm_pcb.h>
|
|
#include <netatm/atm_var.h>
|
|
#include <pci/pcivar.h>
|
|
#include <dev/hfa/fore.h>
|
|
#include <dev/hfa/fore_aali.h>
|
|
#include <dev/hfa/fore_slave.h>
|
|
#include <dev/hfa/fore_stats.h>
|
|
#include <dev/hfa/fore_var.h>
|
|
#include <dev/hfa/fore_include.h>
|
|
|
|
#ifndef lint
|
|
__RCSID("@(#) $FreeBSD$");
|
|
#endif
|
|
|
|
|
|
/*
|
|
* 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%lx\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=%p type=0x%x\n",
|
|
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((volatile 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;
|
|
}
|
|
|