| plan 9 kernel history: overview | file list | diff list |
1991/1101/port/devnonet.c (diff list | history)
| port/devnonet.c on 1990/1210 | ||
| 1990/1210 | #include "u.h" #include "lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "errno.h" | |
| 1991/0328 | #include "../port/nonet.h" | |
| 1990/1210 | ||
| 1990/1229 | #define DPRINT if(pnonet)print | |
| 1990/1210 | #define NOW (MACHP(0)->ticks*MS2HZ) | |
| 1991/0206 | #define MSUCC(x) (((x)+1)%Nnomsg) | |
| 1990/1210 | static Noifc *noifc; | |
| 1990/1229 | int pnonet; | |
| 1990/1210 | enum { /* * tuning parameters */ | |
| 1991/0108 | MSrexmit = 250, /* retranmission interval in ms */ | |
| 1990/1210 | /* * relative or immutable */ Nsubdir = 5, /* entries in the nonet directory */ Nmask = Nnomsg - 1, /* mask for log(Nnomsg) bits */ }; /* predeclared */ | |
| 1991/0119 | static void nohangup(Noconv*); static void noreset(Noconv*); static Block* nohdr(Noconv*, int); static void nolisten(Chan*, Noifc*); static void noannounce(Chan*, char*); static void noconnect(Chan*, char*); static void norack(Noconv*, int); static void nosendctl(Noconv*, int, int); static void nosend(Noconv*, Nomsg*); static void nostartconv(Noconv*, int, char*, int); static void noqack(Noconv*, int); static void nokproc(void*); static void noiput(Queue*, Block*); static void nooput(Queue*, Block*); static void noclose(Queue*); static void noopen(Queue*, Stream*); | |
| 1990/1210 | extern Qinfo noetherinfo; extern Qinfo nonetinfo; /* * nonet directory and subdirectory */ enum { /* * per conversation */ Naddrqid, Nlistenqid, Nraddrqid, Nruserqid, Nstatsqid, Nchanqid, /* * per noifc */ Ncloneqid, }; Dirtab *nonetdir; Dirtab nosubdir[]={ "addr", {Naddrqid}, 0, 0600, "listen", {Nlistenqid}, 0, 0600, "raddr", {Nraddrqid}, 0, 0600, "ruser", {Nruserqid}, 0, 0600, "stats", {Nstatsqid}, 0, 0600, }; /* * Nonet conversation states (for Noconv.state) */ enum { | |
| 1991/0413 | Cclosed= 0, Copen= 1, Cannounced= 2, Cconnected= 3, Cconnecting= 4, Chungup= 5, Creset= 6, | |
| 1990/1210 | }; /* | |
| 1991/0108 | * nonet kproc */ static int kstarted; static Rendez nonetkr; /* | |
| 1990/1210 | * nonet file system. most of the calls use dev.c to access the nonet * directory and stream.c to access the nonet devices. * create the nonet directory. the files are `clone' and stream * directories '1' to '32' (or whatever conf.noconv is in decimal) */ void nonetreset(void) { int i; /* * allocate the interfaces */ noifc = (Noifc *)ialloc(sizeof(Noifc) * conf.nnoifc, 0); | |
| 1990/1229 | for(i = 0; i < conf.nnoifc; i++) | |
| 1990/1210 | noifc[i].conv = (Noconv *)ialloc(sizeof(Noconv) * conf.nnoconv, 0); /* * create the directory. */ nonetdir = (Dirtab *)ialloc(sizeof(Dirtab) * (conf.nnoconv+1), 0); /* * the circuits */ for(i = 0; i < conf.nnoconv; i++) { sprint(nonetdir[i].name, "%d", i); nonetdir[i].qid.path = CHDIR|STREAMQID(i, Nchanqid); nonetdir[i].qid.vers = 0; nonetdir[i].length = 0; nonetdir[i].perm = 0600; } /* * the clone device */ strcpy(nonetdir[i].name, "clone"); nonetdir[i].qid.path = Ncloneqid; nonetdir[i].qid.vers = 0; nonetdir[i].length = 0; nonetdir[i].perm = 0600; } void nonetinit(void) { } /* * find an noifc and increment its count */ Chan* nonetattach(char *spec) { Noifc *ifc; Chan *c; /* * find an noifc with the same name */ if(*spec == 0) spec = "nonet"; for(ifc = noifc; ifc < &noifc[conf.nnoifc]; ifc++){ lock(ifc); if(strcmp(spec, ifc->name)==0 && ifc->wq) { ifc->ref++; unlock(ifc); break; } unlock(ifc); } if(ifc == &noifc[conf.nnoifc]) error(Enoifc); c = devattach('n', spec); c->dev = ifc - noifc; | |
| 1991/0108 | ||
| 1990/1210 | return c; } /* * up the reference count to the noifc */ Chan* nonetclone(Chan *c, Chan *nc) { Noifc *ifc; c = devclone(c, nc); ifc = &noifc[c->dev]; lock(ifc); ifc->ref++; unlock(ifc); return c; } int nonetwalk(Chan *c, char *name) { if(c->qid.path == CHDIR) return devwalk(c, name, nonetdir, conf.nnoconv+1, devgen); else return devwalk(c, name, nosubdir, Nsubdir, streamgen); } void nonetstat(Chan *c, char *dp) { if(c->qid.path == CHDIR) devstat(c, dp, nonetdir, conf.nnoconv+1, devgen); else if(c->qid.path == Ncloneqid) | |
| 1991/0412 | devstat(c, dp, &nonetdir[conf.nnoconv], 1, devgen); | |
| 1990/1210 | else devstat(c, dp, nosubdir, Nsubdir, streamgen); } /* * opening a nonet device allocates a Noconv. Opening the `clone' * device is a ``macro'' for finding a free Noconv and opening * it's ctl file. */ Noconv * nonetopenclone(Chan *c, Noifc *ifc) { Noconv *cp; Noconv *ep; ep = &ifc->conv[conf.nnoconv]; for(cp = &ifc->conv[0]; cp < ep; cp++){ if(cp->state == Cclosed && canqlock(cp)){ if(cp->state != Cclosed){ qunlock(cp); continue; } c->qid.path = CHDIR|STREAMQID(cp-ifc->conv, Nchanqid); devwalk(c, "ctl", 0, 0, streamgen); streamopen(c, &nonetinfo); qunlock(cp); break; } } if(cp == ep) error(Enodev);; return cp; } Chan* nonetopen(Chan *c, int omode) { Stream *s; Noifc *ifc; int line; | |
| 1991/0108 | if(!kstarted){ | |
| 1991/0119 | kproc("nonetack", nokproc, 0); | |
| 1991/0108 | kstarted = 1; } | |
| 1990/1210 | if(c->qid.path & CHDIR){ /* * directories are read only */ if(omode != OREAD) error(Ebadarg); } else switch(STREAMTYPE(c->qid.path)){ case Ncloneqid: /* * get an unused device and open it's control file */ ifc = &noifc[c->dev]; | |
| 1990/1214 | nonetopenclone(c, ifc); | |
| 1990/1210 | break; case Nlistenqid: /* * listen for a call and open the control file for the * channel on which the call arrived. */ line = STREAMID(c->qid.path); ifc = &noifc[c->dev]; if(ifc->conv[line].state != Cannounced) | |
| 1991/0316 | errors("channel not announced"); | |
| 1991/0119 | nolisten(c, ifc); | |
| 1990/1210 | break; case Nraddrqid: /* * read only files */ if(omode != OREAD) error(Ebadarg); break; default: /* * open whatever c points to */ streamopen(c, &nonetinfo); break; } c->mode = openmode(omode); c->flag |= COPEN; c->offset = 0; return c; } void nonetcreate(Chan *c, char *name, int omode, ulong perm) { error(Eperm); } void nonetclose(Chan *c) { Noifc *ifc; /* | |
| 1991/0119 | * real closing happens in noclose | |
| 1990/1210 | */ if(c->qid.path != CHDIR) streamclose(c); /* * let go of the multiplexor */ nonetfreeifc(&noifc[c->dev]); } long | |
| 1991/0411 | nonetread(Chan *c, void *a, long n, ulong offset) | |
| 1990/1210 | { int t; Noconv *cp; char stats[256]; /* * pass data/ctl reads onto the stream code */ t = STREAMTYPE(c->qid.path); if(t >= Slowqid) return streamread(c, a, n); if(c->qid.path == CHDIR) return devdirread(c, a, n, nonetdir, conf.nnoconv+1, devgen); if(c->qid.path & CHDIR) return devdirread(c, a, n, nosubdir, Nsubdir, streamgen); cp = &noifc[c->dev].conv[STREAMID(c->qid.path)]; switch(t){ case Nraddrqid: | |
| 1991/0411 | return stringread(c, a, n, cp->raddr, offset); | |
| 1990/1210 | case Naddrqid: | |
| 1991/0411 | return stringread(c, a, n, cp->addr, offset); | |
| 1990/1210 | case Nruserqid: | |
| 1991/0411 | return stringread(c, a, n, cp->ruser, offset); | |
| 1990/1210 | case Nstatsqid: sprint(stats, "sent: %d\nrcved: %d\nrexmit: %d\nbad: %d\n", cp->sent, cp->rcvd, cp->rexmit, cp->bad); | |
| 1991/0411 | return stringread(c, a, n, stats, offset); | |
| 1990/1210 | } error(Eperm); } long | |
| 1991/0411 | nonetwrite(Chan *c, void *a, long n, ulong offset) | |
| 1990/1210 | { int t; int m; char buf[256]; char *field[5]; t = STREAMTYPE(c->qid.path); /* * get data dispatched as quickly as possible */ if(t == Sdataqid) return streamwrite(c, a, n, 0); /* | |
| 1991/0119 | * easier to do here than in nooput | |
| 1990/1210 | */ if(t == Sctlqid){ strncpy(buf, a, sizeof buf); m = getfields(buf, field, 5, ' '); if(strcmp(field[0], "connect")==0){ if(m < 2) error(Ebadarg); | |
| 1991/0119 | noconnect(c, field[1]); | |
| 1990/1210 | } else if(strcmp(field[0], "announce")==0){ | |
| 1991/0119 | noannounce(c, field[1]); | |
| 1990/1210 | } else if(strcmp(field[0], "accept")==0){ /* ignore */; } else if(strcmp(field[0], "reject")==0){ /* ignore */; } else return streamwrite(c, a, n, 0); return n; } error(Eperm); } void nonetremove(Chan *c) { error(Eperm); } void nonetwstat(Chan *c, char *dp) { error(Eperm); } /* * the device stream module definition */ Qinfo nonetinfo = { | |
| 1991/0119 | noiput, nooput, noopen, noclose, | |
| 1990/1210 | "nonet" }; /* * store the device end of the stream so that the multiplexor can * send blocks upstream. | |
| 1991/0119 | * * State Transition: Cclosed -> Copen | |
| 1990/1210 | */ static void | |
| 1991/0119 | noopen(Queue *q, Stream *s) | |
| 1990/1210 | { Noifc *ifc; Noconv *cp; ifc = &noifc[s->dev]; cp = &ifc->conv[s->id]; cp->s = s; cp->ifc = ifc; cp->rq = RD(q); cp->state = Copen; RD(q)->ptr = WR(q)->ptr = (void *)cp; } | |
| 1991/0320 | ||
| 1990/1210 | static int ishungup(void *a) { Noconv *cp; cp = (Noconv *)a; | |
| 1991/0119 | switch(cp->state){ case Chungup: case Creset: case Cclosed: return 1; } return 0; | |
| 1990/1210 | } | |
| 1991/0320 | /* * wait until a hangup is received. * then send a hangup message (until one is received). * * State Transitions: * -> Cclosed */ | |
| 1990/1210 | static void | |
| 1991/0119 | noclose(Queue *q) | |
| 1990/1210 | { Noconv *cp; Nomsg *mp; int i; cp = (Noconv *)q->ptr; if(waserror()){ cp->rcvcircuit = -1; cp->state = Cclosed; nexterror(); } /* * send hangup messages to the other side * until it hangs up or we get tired. */ | |
| 1991/0119 | switch(cp->state){ case Cconnected: /* * send close till we get one back */ nosendctl(cp, NO_HANGUP, 1); | |
| 1990/1210 | for(i=0; i<10 && !ishungup(cp); i++){ | |
| 1991/0209 | nosendctl(cp, NO_HANGUP, 0); | |
| 1990/1210 | tsleep(&cp->r, ishungup, cp, MSrexmit); } | |
| 1991/0119 | break; case Chungup: /* * ack any close */ nosendctl(cp, NO_HANGUP, 1); break; | |
| 1990/1210 | } | |
| 1991/0108 | qlock(cp); | |
| 1991/0206 | /* * we give up, ack any unacked messages */ for(i = cp->first; i != cp->next; i = MSUCC(i)) norack(cp, cp->out[i].mid); | |
| 1990/1210 | cp->rcvcircuit = -1; cp->state = Cclosed; | |
| 1991/0328 | if(cp->media){ freeb(cp->media); cp->media = 0; } | |
| 1991/0108 | qunlock(cp); | |
| 1991/0206 | ||
| 1990/1210 | poperror(); } /* * send all messages up stream. this should only be control messages | |
| 1991/0119 | * * State Transition: (on M_HANGUP) * -> Chungup | |
| 1990/1210 | */ static void | |
| 1991/0119 | noiput(Queue *q, Block *bp) | |
| 1990/1210 | { Noconv *cp; if(bp->type == M_HANGUP){ cp = (Noconv *)q->ptr; cp->state = Chungup; } PUTNEXT(q, bp); } /* * queue a block */ | |
| 1991/0502 | enum { Window= 1, }; | |
| 1990/1210 | static int windowopen(void *a) { Noconv *cp; | |
| 1991/0207 | int i; | |
| 1990/1210 | cp = (Noconv *)a; | |
| 1991/0207 | i = cp->next - cp->first; | |
| 1991/0502 | if(i>=0 && i<Window) | |
| 1991/0207 | return 1; | |
| 1991/0502 | if(i<0 && Nnomsg+i<Window) | |
| 1991/0207 | return 1; return 0; | |
| 1990/1210 | } static void | |
| 1991/0119 | nooput(Queue *q, Block *bp) | |
| 1990/1210 | { Noconv *cp; int next; Nomsg *mp; | |
| 1991/0926 | Block *first, *last; int len; | |
| 1990/1210 | cp = (Noconv *)(q->ptr); /* * do all control functions */ if(bp->type != M_DATA){ freeb(bp); error(Ebadctl); } /* * collect till we see a delim */ qlock(&cp->mlock); if(!putb(q, bp)){ qunlock(&cp->mlock); return; } | |
| 1991/0206 | /* | |
| 1991/0926 | * take the collected message out of the queue */ first = q->first; last = q->last; len = q->len; q->len = q->nb = 0; q->first = q->last = 0; if(len == 0){ freeb(first); qunlock(&cp->mlock); return; } /* | |
| 1991/0206 | * block till we get an output buffer */ | |
| 1990/1210 | if(waserror()){ | |
| 1991/0926 | freeb(first); | |
| 1991/0109 | qunlock(&cp->mlock); nexterror(); | |
| 1990/1210 | } | |
| 1991/0206 | while(!windowopen(cp)) | |
| 1990/1210 | sleep(&cp->r, windowopen, cp); mp = &cp->out[cp->next]; | |
| 1991/0206 | cp->next = MSUCC(cp->next); qlock(cp); qunlock(&cp->mlock); poperror(); | |
| 1990/1210 | /* | |
| 1991/0206 | * point the output buffer to the message | |
| 1990/1210 | */ mp->time = NOW + MSrexmit; | |
| 1991/0926 | mp->first = first; mp->last = last; mp->len = len; | |
| 1990/1210 | mp->mid ^= Nnomsg; mp->acked = 0; cp->sent++; /* | |
| 1991/01151 | * send the message, the kproc will retry | |
| 1990/1210 | */ | |
| 1991/0925 | if(!waserror()){ nosend(cp, mp); poperror(); | |
| 1991/0206 | } qunlock(cp); | |
| 1990/1210 | } /* * start a new conversation. start an ack/retransmit process if * none already exists for this circuit. */ void | |
| 1991/0119 | nostartconv(Noconv *cp, int circuit, char *raddr, int state) | |
| 1990/1210 | { int i; char name[32]; Noifc *ifc; ifc = cp->ifc; /* * allocate the prototype header */ cp->media = allocb(ifc->hsize + NO_HDRSIZE); cp->media->wptr += ifc->hsize + NO_HDRSIZE; cp->hdr = (Nohdr *)(cp->media->rptr + ifc->hsize); /* * fill in the circuit number */ cp->hdr->flag = NO_NEWCALL|NO_ACKME; cp->hdr->circuit[2] = circuit>>16; cp->hdr->circuit[1] = circuit>>8; cp->hdr->circuit[0] = circuit; /* * set the state variables */ cp->state = state; for(i = 1; i < Nnomsg; i++){ cp->in[i].mid = i; cp->in[i].acked = 0; cp->in[i].rem = 0; cp->out[i].mid = i | Nnomsg; cp->out[i].acked = 1; cp->out[i].rem = 0; } cp->in[0].mid = Nnomsg; cp->in[0].acked = 0; cp->in[0].rem = 0; cp->out[0].mid = 0; cp->out[0].acked = 1; cp->out[0].rem = 0; | |
| 1991/1101 | cp->ack = 0; cp->sendack = 0; | |
| 1990/1210 | cp->first = cp->next = 1; | |
| 1991/0119 | cp->rexmit = cp->bad = cp->sent = cp->rcvd = 0; cp->lastacked = Nnomsg|(Nnomsg-1); | |
| 1990/1210 | /* * used for demultiplexing */ cp->rcvcircuit = circuit ^ 1; /* * media dependent header */ (*ifc->connect)(cp, raddr); /* * status files */ strncpy(cp->raddr, raddr, sizeof(cp->raddr)); strcpy(cp->ruser, "none"); } /* * announce willingness to take calls | |
| 1991/0119 | * * State Transition: Copen -> Chungup | |
| 1990/1210 | */ static void | |
| 1991/0119 | noannounce(Chan *c, char *addr) | |
| 1990/1210 | { Noconv *cp; cp = (Noconv *)(c->stream->devq->ptr); if(cp->state != Copen) error(Einuse); cp->state = Cannounced; } /* * connect to the destination whose name is pointed to by bp->rptr. * * a service is separated from the destination system by a '!' | |
| 1991/0119 | * * State Transition: Copen -> Cconnecting | |
| 1990/1210 | */ static void | |
| 1991/0119 | noconnect(Chan *c, char *addr) | |
| 1990/1210 | { Noifc *ifc; Noconv *cp; char *service; char buf[2*NAMELEN+2]; cp = (Noconv *)(c->stream->devq->ptr); if(cp->state != Copen) error(Einuse); ifc = cp->ifc; service = strchr(addr, '!'); if(service){ *service++ = 0; if(strchr(service, ' ')) error(Ebadctl); if(strlen(service) > NAMELEN) error(Ebadctl); } | |
| 1991/0119 | nostartconv(cp, 2*(cp - ifc->conv), addr, Cconnecting); | |
| 1990/1210 | if(service){ /* * send service name and user name */ cp->hdr->flag |= NO_SERVICE; sprint(buf, "%s %s", service, u->p->pgrp->user); c->qid.path = STREAMQID(STREAMID(c->qid.path), Sdataqid); streamwrite(c, buf, strlen(buf), 1); c->qid.path = STREAMQID(STREAMID(c->qid.path), Sctlqid); } } /* * listen for a call. There can be many listeners, but only one can sleep * on the circular queue at a time. ifc->listenl lets only one at a time into * the sleep. | |
| 1991/0119 | * * State Transition: Cclosed -> Copen -> Cconnecting | |
| 1990/1210 | */ static int iscall(void *a) { Noifc *ifc; ifc = (Noifc *)a; return ifc->rptr != ifc->wptr; } static void | |
| 1991/0119 | nolisten(Chan *c, Noifc *ifc) | |
| 1990/1210 | { Noconv *cp, *ep; Nocall call; int f; char buf[2*NAMELEN+4]; char *user; long n; call.msg = 0; if(waserror()){ if(call.msg) freeb(call.msg); qunlock(&ifc->listenl); nexterror(); } for(;;){ /* * one listener at a time */ qlock(&ifc->listenl); /* * wait for a call */ sleep(&ifc->listenr, iscall, ifc); call = ifc->call[ifc->rptr]; ifc->rptr = (ifc->rptr+1) % Nnocalls; qunlock(&ifc->listenl); /* * make sure we aren't already using this circuit */ ep = &ifc->conv[conf.nnoconv]; for(cp = &ifc->conv[0]; cp < ep; cp++){ | |
| 1991/0112 | if(cp->state>Cannounced && (call.circuit^1)==cp->rcvcircuit | |
| 1990/1210 | && strcmp(call.raddr, cp->raddr)==0) break; } if(cp != ep){ freeb(call.msg); call.msg = 0; continue; } /* * get a free channel */ cp = nonetopenclone(c, ifc); c->qid.path = STREAMQID(cp - ifc->conv, Sctlqid); /* * start the protocol and * stuff the connect message into it */ f = ((Nohdr *)(call.msg->rptr))->flag; | |
| 1991/0108 | DPRINT("call from %d %s\n", call.circuit, call.raddr); | |
| 1991/0119 | nostartconv(cp, call.circuit, call.raddr, Cconnecting); | |
| 1991/0108 | DPRINT("rcving %d byte message\n", call.msg->wptr - call.msg->rptr); | |
| 1990/1210 | nonetrcvmsg(cp, call.msg); call.msg = 0; /* * if a service and remote user were specified, * grab them */ if(f & NO_SERVICE){ | |
| 1991/0108 | DPRINT("reading service\n"); | |
| 1990/1210 | c->qid.path = STREAMQID(cp - ifc->conv, Sdataqid); n = streamread(c, buf, sizeof(buf)); c->qid.path = STREAMQID(cp - ifc->conv, Sctlqid); if(n <= 0) error(Ebadctl); buf[n] = 0; user = strchr(buf, ' '); if(user){ *user++ = 0; strncpy(cp->ruser, user, NAMELEN); } else strcpy(cp->ruser, "none"); strncpy(cp->addr, buf, NAMELEN); } break; } poperror(); } /* * send a hangup signal up the stream to get all line disciplines * to cease and desist | |
| 1991/0119 | * * State Transition: {Cconnected, Cconnecting} -> Chungup | |
| 1990/1210 | */ static void | |
| 1991/0119 | nohangup(Noconv *cp) | |
| 1990/1210 | { Block *bp; Queue *q; | |
| 1991/0119 | switch(cp->state){ case Cconnected: case Cconnecting: cp->state = Chungup; bp = allocb(0); bp->type = M_HANGUP; q = cp->rq; PUTNEXT(q, bp); break; } | |
| 1990/1210 | wakeup(&cp->r); } /* | |
| 1991/0328 | * Send a hangup signal up the stream to get all line disciplines * to cease and desist. Disassociate this conversation from a circuit * number. Any subsequent close of the conversation will * not send hangup messages. | |
| 1991/0119 | * | |
| 1991/0328 | * State Transition: {Cconnected, Cconnecting, Chungup} -> Creset | |
| 1991/0119 | */ static void noreset(Noconv *cp) { Block *bp; Queue *q; switch(cp->state){ case Cconnected: case Cconnecting: | |
| 1991/0328 | case Chungup: | |
| 1991/0119 | cp->state = Creset; | |
| 1991/0328 | cp->rcvcircuit = -1; bp = allocb(0); | |
| 1991/0119 | bp->type = M_HANGUP; q = cp->rq; PUTNEXT(q, bp); break; } wakeup(&cp->r); } /* | |
| 1990/1210 | * process a message acknowledgement. if the message * has any xmit buffers queued, free them. */ static void | |
| 1991/0119 | norack(Noconv *cp, int mid) | |
| 1990/1210 | { Nomsg *mp; Block *bp; int i; mp = &cp->out[mid & Nmask]; /* * if already acked, ignore */ if(mp->acked || mp->mid != mid) return; /* | |
| 1991/01151 | * free it | |
| 1990/1210 | */ | |
| 1991/01151 | cp->rexmit = 0; | |
| 1990/1210 | cp->lastacked = mid; | |
| 1991/0206 | mp->acked = 1; | |
| 1991/0904 | if(mp->first) freeb(mp->first); | |
| 1991/01151 | mp->first = 0; /* * advance first if this is the first */ if((mid&Nmask) == cp->first){ while(cp->first != cp->next){ if(cp->out[cp->first].acked == 0) break; | |
| 1991/0206 | cp->first = MSUCC(cp->first); | |
| 1991/01151 | } | |
| 1991/0206 | wakeup(&cp->r); | |
| 1991/01151 | } | |
| 1990/1210 | } /* * queue an acknowledgement to be sent. ignore it if we already have Nnomsg * acknowledgements queued. */ static void | |
| 1991/0119 | noqack(Noconv *cp, int mid) | |
| 1990/1210 | { | |
| 1991/1101 | cp->ack = mid; cp->sendack = 1; | |
| 1990/1210 | } /* * make a packet header */ Block * | |
| 1991/0119 | nohdr(Noconv *cp, int rem) | |
| 1990/1210 | { Block *bp; Nohdr *hp; bp = allocb(cp->ifc->hsize + NO_HDRSIZE + cp->ifc->mintu); | |
| 1991/0318 | memmove(bp->wptr, cp->media->rptr, cp->ifc->hsize + NO_HDRSIZE); | |
| 1990/1210 | bp->wptr += cp->ifc->hsize + NO_HDRSIZE; hp = (Nohdr *)(bp->rptr + cp->ifc->hsize); hp->remain[1] = rem>>8; hp->remain[0] = rem; hp->sum[0] = hp->sum[1] = 0; | |
| 1991/0318 | ||
| 1990/1210 | return bp; } /* * transmit a message. this involves breaking a possibly multi-block message into * a train of packets on the media. * | |
| 1991/0119 | * called by nooput(). the qlock(mp) synchronizes these two | |
| 1990/1210 | * processes. */ static void | |
| 1991/0119 | nosend(Noconv *cp, Nomsg *mp) | |
| 1990/1210 | { Noifc *ifc; Queue *wq; int msgrem; int pktrem; int n; Block *bp, *pkt, *last; uchar *rptr; ifc = cp->ifc; if(ifc == 0) return; wq = ifc->wq->next; /* * get the next acknowledge to use if the next queue up * is not full. */ | |
| 1991/1101 | cp->sendack = 0; cp->hdr->ack = cp->ack; | |
| 1990/1210 | cp->hdr->mid = mp->mid; | |
| 1991/0118 | /* * package n blocks into m packets. make sure * no packet is < mintu or > maxtu in length. */ | |
| 1990/1210 | if(ifc->mintu > mp->len) { /* * short message: * copy the whole message into the header block */ | |
| 1991/0119 | last = pkt = nohdr(cp, mp->len); | |
| 1990/1210 | for(bp = mp->first; bp; bp = bp->next){ | |
| 1991/0318 | memmove(pkt->wptr, bp->rptr, n = BLEN(bp)); | |
| 1990/1210 | pkt->wptr += n; } | |
| 1991/0118 | /* * round up to mintu */ memset(pkt->wptr, 0, n = ifc->mintu-mp->len); | |
| 1990/1210 | pkt->wptr += n; } else { /* * long message: * break up the message into noifc packets and send them. * once around this loop for each non-header block generated. */ msgrem = mp->len; pktrem = msgrem > ifc->maxtu ? ifc->maxtu : msgrem; bp = mp->first; | |
| 1990/1214 | SET(rptr); | |
| 1990/1210 | if(bp) rptr = bp->rptr; | |
| 1991/0119 | last = pkt = nohdr(cp, msgrem); | |
| 1991/0118 | n = 0; | |
| 1990/1210 | while(bp){ /* * if pkt full, send and create new header block */ if(pktrem == 0){ nonetcksum(pkt, ifc->hsize); last->flags |= S_DELIM; (*wq->put)(wq, pkt); | |
| 1991/0119 | last = pkt = nohdr(cp, -msgrem); | |
| 1990/1210 | pktrem = msgrem > ifc->maxtu ? ifc->maxtu : msgrem; } n = bp->wptr - rptr; if(n > pktrem) n = pktrem; last = last->next = allocb(0); last->rptr = rptr; last->wptr = rptr = rptr + n; msgrem -= n; pktrem -= n; if(rptr >= bp->wptr){ bp = bp->next; if(bp) rptr = bp->rptr; } } | |
| 1991/0118 | /* * round up last packet to mintu */ if(n < ifc->mintu){ n = ifc->mintu - n; last = last->next = allocb(n); memset(last->wptr, 0, n); last->wptr += n; } | |
| 1990/1210 | } nonetcksum(pkt, ifc->hsize); last->flags |= S_DELIM; | |
| 1991/0118 | if(cp->rexmit > 10) mp->time = NOW + 10*MSrexmit; else mp->time = NOW + (cp->rexmit+1)*MSrexmit; | |
| 1991/0318 | ||
| 1990/1210 | (*wq->put)(wq, pkt); } /* * send a control message (hangup or acknowledgement). */ static void | |
| 1991/0119 | nosendctl(Noconv *cp, int flag, int new) | |
| 1990/1210 | { | |
| 1991/0102 | Nomsg ctl; ctl.len = 0; ctl.first = 0; ctl.acked = 0; | |
| 1990/1210 | if(new) | |
| 1991/0102 | ctl.mid = Nnomsg^cp->out[cp->next].mid; | |
| 1990/1210 | else | |
| 1991/0102 | ctl.mid = cp->lastacked; | |
| 1990/1210 | cp->hdr->flag |= flag; | |
| 1991/0119 | nosend(cp, &ctl); | |
| 1990/1210 | } /* * receive a message (called by the multiplexor; noetheriput, nofddiiput, ...) | |
| 1991/0119 | * * State Transition: (no NO_NEWCALL in msg) Cconnecting -> Cconnected | |
| 1990/1210 | */ void nonetrcvmsg(Noconv *cp, Block *bp) { Block *nbp; Nohdr *h; short r; int c; Nomsg *mp; int f; Queue *q; q = cp->rq; /* * grab the packet header, push the pointer past the nonet header */ h = (Nohdr *)bp->rptr; bp->rptr += NO_HDRSIZE; mp = &cp->in[h->mid & Nmask]; r = (h->remain[1]<<8) | h->remain[0]; f = h->flag; /* | |
| 1991/0328 | * Obey reset even if the message id is bogus | |
| 1990/1210 | */ | |
| 1991/0328 | if(f & NO_RESET){ | |
| 1991/0608 | DPRINT("reset received\n"); | |
| 1991/0119 | noreset(cp); | |
| 1991/0328 | freeb(bp); | |
| 1990/1210 | return; } /* | |
| 1991/0328 | * A new call request (maybe), treat it as a reset if seen on a * connected, hungup, or reset channel. * * On a connecting channel, treat as a reset if this is an * invalid message ID. */ if(f & NO_NEWCALL){ switch(cp->state){ case Cclosed: case Copen: case Cannounced: case Creset: | |
| 1991/0608 | DPRINT("nonetrcvmsg %d %d\n", cp->rcvcircuit, cp - cp->ifc->conv); freeb(bp); return; | |
| 1991/0328 | case Chungup: case Cconnected: | |
| 1991/0608 | DPRINT("Nonet call on connected/hanging-up circ %d conv %d\n", | |
| 1991/0328 | cp->rcvcircuit, cp - cp->ifc->conv); freeb(bp); noreset(cp); return; case Cconnecting: if(h->mid != mp->mid){ | |
| 1991/0608 | DPRINT("Nonet call on connecting circ %d conv %d\n", | |
| 1991/0328 | cp->rcvcircuit, cp - cp->ifc->conv); freeb(bp); noreset(cp); return; } break; } } /* | |
| 1990/1210 | * ignore old messages and process the acknowledgement */ | |
| 1991/0606 | if(h->mid!=mp->mid || (f&NO_NULL)){ | |
| 1991/0108 | DPRINT("old msg %d instead of %d r==%d\n", h->mid, mp->mid, r); | |
| 1990/1210 | if(r == 0){ | |
| 1991/0119 | norack(cp, h->ack); | |
| 1991/0328 | if(f & NO_HANGUP) | |
| 1991/0119 | nohangup(cp); | |
| 1990/1210 | } else { | |
| 1991/0102 | if(r>0){ | |
| 1991/0119 | norack(cp, h->ack); noqack(cp, h->mid); | |
| 1991/0102 | } | |
| 1990/1210 | cp->bad++; } freeb(bp); return; } if(r>=0){ /* * start of message packet */ if(mp->first){ | |
| 1991/01151 | DPRINT("mp->mid==%d mp->rem==%d r==%d\n", mp->mid, mp->rem, r); freeb(mp->first); mp->first = mp->last = 0; mp->len = 0; | |
| 1990/1210 | } mp->rem = r; } else { /* * a continuation */ if(-r != mp->rem) { | |
| 1991/01151 | DPRINT("mp->mid==%d mp->rem==%d r==%d\n", mp->mid, mp->rem, r); | |
| 1990/1210 | cp->bad++; freeb(bp); return; } } /* * take care of packets that were padded up */ mp->rem -= BLEN(bp); if(mp->rem < 0){ if(-mp->rem <= BLEN(bp)){ bp->wptr += mp->rem; mp->rem = 0; } else panic("nonetrcvmsg: short packet"); } putb(mp, bp); /* * if the last chunk - pass it up the stream and wake any * waiting process. * * if not, strip off the delimiter. */ if(mp->rem == 0){ | |
| 1991/0119 | cp->hdr->flag &= ~(NO_NEWCALL|NO_SERVICE); norack(cp, h->ack); | |
| 1990/1210 | if(f & NO_ACKME) | |
| 1991/0119 | noqack(cp, h->mid); | |
| 1990/1210 | mp->last->flags |= S_DELIM; PUTNEXT(q, mp->first); mp->first = mp->last = 0; mp->len = 0; cp->rcvd++; /* * cycle bufffer to next expected mid */ mp->mid ^= Nnomsg; /* | |
| 1991/0119 | * any NO_NEWCALL after this is another call | |
| 1990/1210 | */ | |
| 1991/0119 | if(cp->state==Cconnecting && !(f&NO_NEWCALL)) | |
| 1990/1210 | cp->state = Cconnected; | |
| 1991/0119 | /* * hangup (after processing message) */ | |
| 1991/0328 | if(f & NO_HANGUP){ | |
| 1991/0119 | DPRINT("hangup with message\n"); nohangup(cp); } | |
| 1990/1210 | } else mp->last->flags &= ~S_DELIM; } | |
| 1991/0119 | ||
| 1990/1210 | /* * noifc */ /* * Create an ifc. */ Noifc * nonetnewifc(Queue *q, Stream *s, int maxtu, int mintu, int hsize, void (*connect)(Noconv *, char *)) { Noifc *ifc; int i; for(ifc = noifc; ifc < &noifc[conf.nnoifc]; ifc++){ if(ifc->ref == 0){ lock(ifc); if(ifc->ref) { /* someone was faster than us */ unlock(ifc); continue; } RD(q)->ptr = WR(q)->ptr = (void *)ifc; for(i = 0; i < conf.nnoconv; i++) ifc->conv[i].rcvcircuit = -1; ifc->maxtu = maxtu - hsize - NO_HDRSIZE; ifc->mintu = mintu - hsize - NO_HDRSIZE; ifc->hsize = hsize; ifc->connect = connect; ifc->name[0] = 0; ifc->wq = WR(q); ifc->ref = 1; unlock(ifc); return ifc; } } error(Enoifc); } /* * Free an noifc. */ void nonetfreeifc(Noifc *ifc) { lock(ifc); ifc->ref--; if(ifc->ref == 0) ifc->wq = 0; unlock(ifc); } /* * calculate the checksum of a list of blocks. ignore the first `offset' bytes. */ int nonetcksum(Block *bp, int offset) { uchar *ep, *p; int n; ulong s; Nohdr *hp; | |
| 1991/0502 | Block *first; | |
| 1990/1210 | s = 0; p = bp->rptr + offset; n = bp->wptr - p; hp = (Nohdr *)p; hp->sum[0] = hp->sum[1] = 0; for(;;){ ep = p+(n&~0x7); while(p < ep) { s = s + s + p[0]; s = s + s + p[1]; s = s + s + p[2]; s = s + s + p[3]; s = s + s + p[4]; s = s + s + p[5]; s = s + s + p[6]; s = s + s + p[7]; s = (s&0xffff) + (s>>16); p += 8; } ep = p+(n&0x7); while(p < ep) { s = s + s + *p; p++; } s = (s&0xffff) + (s>>16); bp = bp->next; if(bp == 0) break; p = bp->rptr; n = BLEN(bp); } s = (s&0xffff) + (s>>16); hp->sum[1] = s>>8; hp->sum[0] = s; | |
| 1991/0502 | s &= 0xffff; | |
| 1991/0503 | if(0) | |
| 1991/0502 | switch(s){ case 0xac9f: case 0xc1a4: case 0xc41c: case 0xc46d: { int i; print("%lux s,", s); for(bp = first; bp; bp = bp->next) for(i = 0; i < BLEN(bp); i++) print(" %ux", bp->rptr[i]); } } return s; | |
| 1990/1229 | } | |
| 1991/0108 | /* * send acknowledges that need to be sent. this happens at 1/2 * the retransmission interval. */ static void | |
| 1991/0119 | nokproc(void *arg) | |
| 1991/0108 | { Noifc *ifc; Noconv *cp, *ep; | |
| 1991/0118 | Nomsg *mp; | |
| 1991/0108 | cp = 0; ifc = 0; if(waserror()){ if(ifc) unlock(ifc); if(cp) qunlock(cp); } | |
| 1991/0926 | for(;;){ | |
| 1991/01151 | /* | |
| 1991/0926 | * loop through all active interfaces | |
| 1991/01151 | */ | |
| 1991/0926 | for(ifc = noifc; ifc < &noifc[conf.nnoifc]; ifc++){ if(ifc->wq==0 || !canlock(ifc)) | |
| 1991/0108 | continue; | |
| 1991/0926 | ||
| 1991/01151 | /* | |
| 1991/0926 | * loop through all active conversations | |
| 1991/01151 | */ | |
| 1991/0926 | ep = ifc->conv + conf.nnoconv; for(cp = ifc->conv; cp < ep; cp++){ if(cp->state<=Copen || !canqlock(cp)) continue; if(cp->state <= Copen){ qunlock(cp); continue; } /* * resend the first message */ if(cp->first!=cp->next && NOW>=cp->out[cp->first].time){ mp = &(cp->out[cp->first]); if(cp->rexmit++ > 15){ norack(cp, mp->mid); noreset(cp); } else nosend(cp, mp); } /* * get the acknowledges out */ | |
| 1991/1101 | if(cp->sendack){ DPRINT("sending ack %d\n", cp->ack); | |
| 1991/0926 | nosendctl(cp, /*NO_NULL*/0, 0); } qunlock(cp); | |
| 1991/01151 | } | |
| 1991/0926 | unlock(ifc); | |
| 1991/0108 | } | |
| 1991/0926 | tsleep(&nonetkr, return0, 0, MSrexmit/2); | |
| 1991/0108 | } } | |
| 1991/01151 | void nonettoggle(void) | |
| 1990/1229 | { pnonet ^= 1; | |
| 1990/1210 | } | |
| 1991/0108 | ||