/* * C H A O S L D * * Chaosnet line discipline, to be pushed on an ethernet controller. * Handles xmit and rcv windows, packet numbering, and connection states. * * * (c) Copyright 1985 Nirvonics, Inc. * * Written by Kurt Gollhardt * Last update Tue Mar 12 04:18:58 1985 * * This software is the property of Nirvonics, Inc. * All rights reserved. * */ #include "ch.h" #if NCH #include "../h/param.h" #include "../h/systm.h" #include "../h/stream.h" #include "../h/ioctl.h" #include "../h/ttyld.h" #include "../h/map.h" #include "../h/buf.h" #include "../h/conf.h" #include "../chaosld/types.h" #define CHDEFINE #include "../chaosld/globals.h" extern int chrcv(), chb_put(); int chopen(), chclose(), chosrv(), chisrv(); static struct qinit chrinit = { chb_put, chisrv, chopen, chclose, 512, 64 }; static struct qinit chwinit = { putq, chosrv, chopen, chclose, 512, 64 }; struct streamtab chinfo = { &chrinit, &chwinit }; chopen(q, dev) register struct queue *q; { if (q->ptr) return(1); if (ChaosQ != (struct queue *)0) /* Already open */ return(0); ChaosQ = q; /* that's the RD q */ q->flag |= QDELIM|QNOENB; /* chiput calls qenable() */ WR(q)->flag |= QDELIM; return(1); } chclose(q) struct queue *q; { struct connection **connp; int ps = spl6(); for (connp = Chconn; connp < &Chconn[NCH]; ++connp) if (*connp != NOCONN) chld_close(*connp, NOPKT); freelist(Chlsnlist); freelist(Chrfclist); Chlsnlist = Chrfclist = Chrfctail = NOPKT; Chmyaddr.ch_addr = 0; ChaosQ = (struct queue *)0; splx(ps); } chisrv(q) struct queue *q; { register struct block *bp; chrcv(q); /* Receive the packet */ /* See if there's another packet on the queue */ for (bp = q->first; bp != NOBLOCK; bp = bp->next) if (bp->type == M_DELIM) { qenable(q); /* re-enable queue for the next packet */ break; } } chosrv(q) register struct queue *q; { register union stmsg *sp; register struct block *bp; while(bp = getq(q)){ if(bp->type == M_IOCTL){ sp = (union stmsg *)bp->rptr; switch(sp->ioc0.com){ case CHIOCNAME: copyin(*(caddr_t *)sp->iocx.xxx, Chmyname, CHSTATNAME); bp->rptr = bp->wptr; bp->type = M_IOCACK; qreply(q, bp); break; case CHIOCADDR: Chmyaddr.ch_addr = *(int *)sp->iocx.xxx; /* fall through */ default: (*q->next->qinfo->putp)(q->next, bp); break; } } else (*q->next->qinfo->putp)(q->next, bp); } } chld_write(conn, pkt) register struct connection *conn; register struct packet *pkt; { setconn(conn, pkt); switch (pkt->pk_op) { case ANSOP: case FWDOP: if (conn->cn_state != CSRFCRCVD || (conn->cn_flags & CHANSWER) == 0) goto err; chld_close(conn, pkt); return 0; case RFCOP: case BRDOP: if (conn->cn_state != CSRFCSENT) goto err; break; case UNCOP: pkt->pk_pkn = 0; pkt->next = NOPKT; senddata(pkt); return 0; default: if (!ISDATA(pkt)) goto err; case OPNOP: case EOFOP: if (conn->cn_state != CSOPEN) goto err; setack(conn, pkt); break; } pkt->pk_pkn = ++conn->cn_tlast; senddata(pkt); return 0; err: free_packet(pkt); return -1; } chld_eof(conn) struct connection *conn; { register struct packet *pkt; if ((pkt = new_packet()) != NOPKT) { setconn(conn, pkt); pkt->pk_op = EOFOP; return chld_write(conn, pkt); } return 0; } chld_accept(conn) struct connection *conn; { conn->cn_state = CSOPEN; sendopn(conn); } chld_open(conn, daddr, rwsize, pkt) register struct connection *conn; register struct packet *pkt; { conn->cn_faddr.ch_addr = daddr; conn->cn_state = CSRFCSENT; conn->cn_rwsize = rwsize; conn->cn_rsts = rwsize / 2; conn->cn_time = Chclock; pkt->pk_op = RFCOP; pkt->pk_fc = pkt->pk_ackn = 0; debug(DCONN,printf("Conn #%x: RFCS state\n", conn->cn_lidx.ci_idx)); /* * By writing the RFC packet like a data packet, it will be kept * around for automatic retransmission until freed by the normal * receipting mechanism; xmitdone() and chrcv() facilitate this. * Since new_conn() clears the connection (including cn_tlast), * the packet number of the RFC will be 1 (pkn = ++cn_tlast). */ chld_write(conn, pkt); } chld_listen(conn, rwsize, pkt) register struct connection *conn; register struct packet *pkt; { register struct packet *opkt, *pktl; int ps; conn->cn_state = CSLISTEN; conn->cn_flags |= CHSERVER; conn->cn_rwsize = rwsize; pkt->pk_op = LSNOP; setconn(conn, pkt); if (flatten(pkt)) { free_packet(pkt); return; } ps = spl6(); opkt = NOPKT; for (pktl = Chrfclist; pktl != NOPKT; pktl = (opkt = pktl)->next) if (concmp(pktl, pkt->data->rptr, (int)pkt->pk_len)) { if (opkt == NOPKT) Chrfclist = pktl->next; else opkt->next = pktl->next; if (pktl == Chrfctail) Chrfctail = opkt; free_packet(pkt); lsnmatch(pktl, conn); chd_newstate(conn); splx(ps); return; } pkt->next = Chlsnlist; Chlsnlist = pkt; splx(ps); debug(DCONN,printf("Conn #%x: LISTEN state\n", conn->cn_lidx.ci_idx)); } chld_close(conn, pkt) register struct connection *conn; register struct packet *pkt; { int ps = spl6(); ch_busy++; switch (conn->cn_state) { case CSOPEN: case CSRFCRCVD: if (pkt != NOPKT) { setconn(conn, pkt); pkt->pk_ackn = pkt->pk_pkn = 0; sendctl(pkt); pkt = NOPKT; } /* Fall into ... */ case CSRFCSENT: close_conn(conn, CSCLOSED, NOPKT); break; case CSLISTEN: rmlisten(conn); break; } --ch_busy; splx(ps); if (pkt != NOPKT) free_packet(pkt); } #endif