# #include "../h/param.h" #include "../h/buf.h" #include "../h/pkts.h" #include "../h/net.h" /* Rcv i/o control block * Interlock via B_BUSY bit * Buffer locked by netr_start(at spl6) * Buffer unlocked by netrcv(at spl4), netr_start(at spl4), * or netr_flush(net ints off) */ struct buf rcvbuf; /* Rcvpktdir receive packet directory * Rcvmem receive packet memory * The receive packet queue is searched * and modified at interrupt time as * well as non-interrupt time; therefore * receive net interrupts must be locked * out when queue is searched or modified */ struct rcvpktdir rcvpktdir; #define NRCVPKT 5 struct rcvpkt rcvmem[NRCVPKT]; short neterr[MAXNERR + 1]; netr_init() { register struct rcvpktdir *rpd; rpd = &rcvpktdir; rpd->rd_flags = 0; rpd->rd_begin = &rcvmem[0]; rpd->rd_end = &rcvmem[NRCVPKT]; rpd->rd_head = NULL; rpd->rd_tail = NULL; rpd->rd_rcvenb = NULL; } /* netr_start net receive start * Set rcv control block BUSY flag as interlock * Allocate rcv memory and invoke net * device receive enable routine * netr_start invoked at interrupt as well * as non-interrupt level, so calling * routine must spl down as appropriate */ netr_start() { register struct buf *bp; bp = &rcvbuf; spl6(); if ((bp->b_flags & B_BUSY) || (neti_que.q_cnt == 0) ) { spl4(); return; } bp->b_flags = (B_BUSY|B_PHYS|B_READ); spl4(); if (!netr_malloc(bp)) bp->b_flags = 0; else viircv(bp); } /* netr_malloc allocate memory * in circular buffer for net receive */ netr_malloc(bp) struct buf *bp; { register struct rcvpktdir *rdp; register char *freeb, *freee; rdp = &rcvpktdir; if (rdp->rd_head == NULL) { freeb = rdp->rd_begin; goto done; } freeb = ((char *) rdp->rd_tail) + (RCVPKTHDSZ + rdp->rd_tail->rp_len); freee = ((char *) rdp->rd_head >= freeb) ? (char *) rdp->rd_head : rdp->rd_end; while ((freee - freeb) < RCVPKTSZ) { if (freee == (char *) rdp->rd_head) { if (rdp->rd_head->rp_flags & N_LOCK) { rdp->rd_head->rp_flags |= N_WANTED; return(FALSE); } #ifdef DEBUG printf("Drop pkt freeb %o freee %o pktsiz %o\n", freeb, freee, RCVPKTSZ); show_npq(); #endif neterrt(DROPP); netp_deque(rdp->rd_head); if (rdp->rd_head == NULL) { freeb = rdp->rd_begin; break; } freee = ((char *) rdp->rd_head >= freeb) ? (char *) rdp->rd_head : rdp->rd_end; } else { freeb = rdp->rd_begin; freee = (char *) rdp->rd_head; } } done: netr_mclr(freeb); rdp->rd_rcvenb = freeb; bp->b_addr = &((struct rcvpkt *) freeb)->rp_net; bp->b_wcount = - ((NETBYTES >> 1) & 077777); return(TRUE); } netr_mclr(cstart) char *cstart; { register int *is, *ie; for (is = cstart, ie = cstart + RCVPKTSZ - (IPDATASZ - 20); is < ie; ) *is++ = 0; } /* netrcv handle packet just received from net * chk local and inet hdrs * invoke mng_frag_disp to handle reassembly and * to dispatch complete packet to its owner process */ netrcv(bp) register struct buf *bp; { register struct rcvpkt *rp; register char *ppkt; register struct netino *nip; if (bp->b_flags & B_ERROR) goto done; rp = rcvpktdir.rd_rcvenb; ppkt = bp->b_addr; if ((ppkt - (char *) rp) != RCVPKTHDSZ) { neterrp("Netrcv weird pkt loc", rp, ppkt, PROGERR); goto done; } rp->rp_len = ((-bp->b_wcount) + bp->b_resid) << 1; if ( !ch_net(rp) || !ch_inet(&rp->rp_inet, (rp->rp_len - NETHSIZ)) ) goto done; mng_frag_disp(rp); done: rcvpktdir.rd_rcvenb = NULL; bp->b_flags = 0; return; } netr_flush(bp) struct buf *bp; { rcvpktdir.rd_rcvenb = NULL; bp->b_flags = 0; return; } neterrt(ecode) short ecode; { neterr[ecode]++; } neterrp(emsg, err1, err2, ecode) char *emsg; short err1, err2, ecode; { printf("NETERR %s %o %o\n", emsg, err1, err2); neterr[ecode]++; } /* netp_enque put packet in rcvmem, point to * by rp, at end of filled packet queue */ netp_enque(rp) register struct rcvpkt *rp; { register struct rcvpktdir *rdp; rdp = &rcvpktdir; rp->rp_forw = NULL; rp->rp_back = rdp->rd_tail; if (rdp->rd_head == NULL) rdp->rd_head = rp; else rdp->rd_tail->rp_forw = rp; rdp->rd_tail = rp; } /* netp_deque remove packet in rcvmem, * pointed to by rp, from filled packet queue */ netp_deque(rp) register struct rcvpkt *rp; { register struct rcvpktdir *rdp; rdp = &rcvpktdir; if (rp->rp_back == NULL) rdp->rd_head = rp->rp_forw; /* first on queue */ else rp->rp_back->rp_forw = rp->rp_forw; if (rp->rp_forw == NULL) rdp->rd_tail = rp->rp_back; /* last on queue */ else rp->rp_forw->rp_back = rp->rp_back; } /* neti_pdque remove packets from filled pkt queue in rcvmem * which are destined for this net inode */ neti_pdque(nip) register struct netino *nip; { register struct rcvpkt *rp; for (rp = rcvpktdir.rd_head; rp != NULL; rp = rp->rp_forw) if (nip == rp->rp_inode) netp_deque(rp); } #ifdef DEBUG show_npq() { register struct rcvpktdir *rdp; register struct rcvpkt *rp; register char *ic; int i; rdp = &rcvpktdir; printf("Rcvpktdir %o %o %o %o %o %o\n", rdp->rd_flags, rdp->rd_begin, rdp->rd_end, rdp->rd_head, rdp->rd_tail, rdp->rd_rcvenb); for (rp = rdp->rd_head; rp != NULL; rp = rp->rp_forw) { printf("rp %o flgs %o len %o ino %o\nInet ", rp, rp->rp_flags, rp->rp_len, rp->rp_inode); for (ic = &rp->rp_inet, i = 0; i < 20; i++) printf("%o ", *ic++); printf("\n"); } } #endif