| plan 9 kernel history: overview | file list | diff list |
1991/1206/port/devdk.c (diff list | history)
| port/devdk.c on 1990/0312 | ||
| 1990/0312 | #include "u.h" #include "lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "errno.h" | |
| 1990/11161 | #define DPRINT if(0) print | |
| 1990/0312 | ||
| 1990/0911 | #define NOW (MACHP(0)->ticks) | |
| 1990/0312 | typedef struct Dkmsg Dkmsg; typedef struct Line Line; typedef struct Dk Dk; | |
| 1991/1115 | enum { Maxlines = 256, }; | |
| 1990/0312 | /* * types of possible dkcalls */ enum { Dial, Announce, Redial }; /* * format of messages to/from the datakit controller on the common * signalling line */ struct Dkmsg { uchar type; uchar srv; uchar param0l; uchar param0h; uchar param1l; uchar param1h; uchar param2l; uchar param2h; uchar param3l; uchar param3h; uchar param4l; uchar param4h; }; /* * message codes (T_xxx == dialin.type, D_xxx == dialin.srv) */ #define T_SRV 1 /* service request */ #define D_SERV 1 /* (host to dkmux) announce a service */ #define D_DIAL 2 /* (host to dkmux) connect to a service */ #define D_XINIT 7 /* (dkmux to host) line has been spliced */ #define T_REPLY 2 /* reply to T_SRV/D_SERV or T_SRV/D_DIAL */ #define D_OK 1 /* not used */ #define D_OPEN 2 /* (dkmux to host) connection established */ #define D_FAIL 3 /* (dkmux to host) connection failed */ | |
| 1990/0617 | #define T_CHG 3 /* change the status of a connection */ | |
| 1990/0312 | #define D_CLOSE 1 /* close the connection */ #define D_ISCLOSED 2 /* (dkmux to host) confirm a close */ #define D_CLOSEALL 3 /* (dkmux to host) close all connections */ #define D_REDIAL 6 /* (host to dkmux) redial a call */ #define T_ALIVE 4 /* (host to dkmux) keep alive message */ #define D_CONTINUE 0 /* host has not died since last msg */ #define D_RESTART 1 /* host has restarted */ #define D_MAXCHAN 2 /* request maximum line number */ #define T_RESTART 8 /* (dkmux to host) datakit restarted */ /* * macros for cracking/forming the window negotiation parameter */ #define MIN(x,y) (x < y ? x : y) #define W_WINDOW(o,d,t) ((o<<8) | (d<<4) | t | 0100000) #define W_VALID(x) ((x) & 0100000) #define W_ORIG(x) (((x)>>8) & 017) #define W_DEST(x) (((x)>>4) & 017) #define W_TRAF(x) ((x) & 017) #define W_DESTMAX(x,y) (W_WINDOW(W_ORIG(x),MIN(W_DEST(x),y),W_TRAF(x))) #define W_LIMIT(x,y) (W_WINDOW(MIN(W_ORIG(x),y),MIN(W_DEST(x),y),W_TRAF(x))) #define W_VALUE(x) (1<<((x)+4)) #define WS_2K 7 struct Line { QLock; | |
| 1991/1107 | int lineno; | |
| 1990/0312 | Rendez r; /* wait here for dial */ int state; /* dial state */ int err; /* dialing error (if non zero) */ int window; /* negotiated window */ int timestamp; /* timestamp of last call received on this line */ int calltolive; /* multiple of 15 seconds for dialing state to last */ Queue *rq; char addr[64]; char raddr[64]; char ruser[32]; Dk *dp; /* interface contianing this line */ }; /* * a dkmux dk. one exists for every stream that a | |
| 1990/0315 | * dkmux line discipline is pushed onto. | |
| 1990/0312 | */ struct Dk { | |
| 1991/1116 | QLock; Chan *csc; | |
| 1990/1101 | Lock; | |
| 1991/1120 | int ref; | |
| 1990/1101 | int opened; | |
| 1991/1116 | ||
| 1990/1018 | char name[64]; /* dk name */ | |
| 1990/0312 | Queue *wq; /* dk output queue */ | |
| 1990/1101 | Stream *s; | |
| 1990/0312 | int ncsc; /* csc line number */ | |
| 1991/1116 | int lines; /* number of lines */ | |
| 1991/1107 | Line **linep; | |
| 1990/0707 | int restart; | |
| 1990/0911 | int urpwindow; | |
| 1990/1101 | Rendez timer; | |
| 1990/1210 | int closeall; /* set when we receive a closeall message */ Rendez closeallr; /* wait here for a closeall */ | |
| 1991/1108 | Network net; | |
| 1991/1115 | Netprot *prot; | |
| 1991/1107 | ||
| 1991/1116 | Block *alloc; /* blocks containing Line structs */ | |
| 1990/0312 | }; | |
| 1991/1107 | static Dk *dk; | |
| 1990/1101 | static Lock dklock; | |
| 1990/0312 | /* * conversation states (for Line.state) */ typedef enum { Lclosed=0, Lopened, /* opened but no call out */ Lconnected, /* opened and a call set up on htis line */ Lrclose, /* remote end has closed down */ Llclose, /* local end has closed down */ Ldialing, /* dialing a new call */ Llistening, /* this line listening for calls */ Lackwait, /* incoming call waiting for ack/nak */ Laccepting, /* waiting for user to accept or reject the call */ } Lstate; /* | |
| 1991/1206 | * map datakit error to errno | |
| 1990/0312 | */ enum { DKok, DKbusy, DKnetotl, DKdestotl, DKbadnet, DKnetbusy, DKinuse, DKreject, }; | |
| 1991/1206 | char* dkerr[]={ [DKok]"", [DKbusy]"host overloaded", [DKnetotl]"network not answering", [DKdestotl]"host not answering", [DKbadnet]"unknown address", [DKnetbusy]"network overloaded", [DKinuse]"server in use", [DKreject]"connection refused", | |
| 1990/0312 | }; | |
| 1991/1206 | #define DKERRS sizeof(dkerr)/sizeof(char*) | |
| 1990/0312 | /* * imported */ extern Qinfo urpinfo; /* * predeclared */ Chan* dkattach(char*); | |
| 1990/1101 | static void dkmuxconfig(Queue*, Block*); static Chan* dkopenline(Dk*, int); | |
| 1991/1116 | static Chan* dkopencsc(Dk*); | |
| 1990/1101 | static int dkmesg(Chan*, int, int, int, int); | |
| 1990/0312 | static void dkcsckproc(void*); static void dkanswer(Chan*, int, int); static void dkwindow(Chan*); static void dkcall(int, Chan*, char*, char*, char*); static void dktimer(void*); | |
| 1990/1101 | static void dkchgmesg(Chan*, Dk*, Dkmsg*, int); | |
| 1990/0312 | static void dkreplymesg(Dk*, Dkmsg*, int); Chan* dkopen(Chan*, int); | |
| 1990/1101 | static void dkhangup(Line*); | |
| 1990/0312 | /* | |
| 1991/1108 | * for standard network interface (net.c) */ static int dkcloneline(Chan*); static int dklisten(Chan*); static void dkfilladdr(Chan*, char*, int); static void dkfillraddr(Chan*, char*, int); static void dkfillruser(Chan*, char*, int); extern Qinfo dkinfo; /* | |
| 1990/0312 | * the datakit multiplexor stream module definition */ static void dkmuxopen(Queue *, Stream *); static void dkmuxclose(Queue *); static void dkmuxoput(Queue *, Block *); static void dkmuxiput(Queue *, Block *); | |
| 1990/1113 | Qinfo dkmuxinfo = { dkmuxiput, dkmuxoput, dkmuxopen, dkmuxclose, "dkmux" }; | |
| 1990/0312 | /* | |
| 1990/1101 | * Look for a dk struct with a name. If none exists, create one. */ static Dk * | |
| 1991/1107 | dkalloc(char *name, int ncsc, int lines) | |
| 1990/0312 | { Dk *dp; | |
| 1990/1101 | Dk *freep; | |
| 1991/1107 | Block *bp; int i, n; | |
| 1991/1108 | Line *lp; | |
| 1990/0312 | ||
| 1990/1101 | lock(&dklock); freep = 0; | |
| 1991/1107 | for(dp = dk; dp < &dk[conf.dkif]; dp++){ | |
| 1990/1101 | if(strcmp(name, dp->name) == 0){ unlock(&dklock); return dp; | |
| 1990/0312 | } | |
| 1991/1120 | if(dp->name[0] == 0 && dp->ref == 0) | |
| 1990/1101 | freep = dp; | |
| 1990/0312 | } | |
| 1990/1101 | if(freep == 0){ unlock(&dklock); | |
| 1990/11211 | error(Enoifc); | |
| 1990/1101 | } | |
| 1991/1108 | if(lines == 0){ unlock(&dklock); | |
| 1991/1107 | errors("unknown dk interface"); | |
| 1991/1108 | } | |
| 1991/1107 | /* * init the structures */ | |
| 1990/1101 | dp = freep; dp->opened = 0; dp->s = 0; | |
| 1991/1107 | dp->ncsc = ncsc; dp->lines = lines; | |
| 1990/1101 | strncpy(dp->name, name, sizeof(freep->name)); | |
| 1991/1107 | /* * allocate memory for line structures */ | |
| 1991/1115 | n = sizeof(Line*)*(dp->lines); | |
| 1991/1107 | bp = allocb(n); if(bp->lim - bp->base < n){ unlock(&dklock); errors("too many lines"); } | |
| 1991/1108 | memset(bp->base, 0, bp->lim-bp->base); | |
| 1991/1107 | dp->linep = (Line **)bp->base; bp->wptr += n; dp->alloc = bp; | |
| 1991/1108 | n = sizeof(Line)*(dp->lines); | |
| 1991/1115 | for(i = 0; i < dp->lines; i++){ | |
| 1991/1107 | if(bp->lim - bp->wptr < sizeof(Line)){ bp = allocb(sizeof(Line)*n); | |
| 1991/1108 | memset(bp->base, 0, bp->lim-bp->base); | |
| 1991/1107 | bp->next = dp->alloc; dp->alloc = bp; } dp->linep[i] = (Line*)bp->wptr; dp->linep[i]->lineno = i; bp->wptr += sizeof(Line); | |
| 1991/1108 | n -= sizeof(Line); | |
| 1991/1107 | } | |
| 1991/1108 | /* * fill in the network structure */ dp->net.name = dp->name; | |
| 1991/1115 | dp->net.nconv = dp->lines; | |
| 1991/1108 | dp->net.devp = &dkinfo; dp->net.protop = &urpinfo; dp->net.listen = dklisten; dp->net.clone = dkcloneline; | |
| 1991/1115 | dp->net.prot = dp->prot; | |
| 1991/1122 | dp->net.ninfo = 4; | |
| 1991/1108 | dp->net.info[0].name = "addr"; dp->net.info[0].fill = dkfilladdr; dp->net.info[1].name = "raddr"; dp->net.info[1].fill = dkfillraddr; dp->net.info[2].name = "ruser"; dp->net.info[2].fill = dkfillruser; | |
| 1991/1122 | dp->net.info[3].name = "stats"; dp->net.info[3].fill = urpfillstats; | |
| 1991/1108 | ||
| 1990/1101 | unlock(&dklock); return dp; | |
| 1990/0312 | } /* | |
| 1990/1101 | * a new dkmux. find a free dk structure and assign it to this queue. * when we get though here dp->s is meaningful and the name is set to "/". */ static void dkmuxopen(Queue *q, Stream *s) { RD(q)->ptr = s; WR(q)->ptr = 0; } /* | |
| 1990/0312 | * close down a dkmux */ static void dkmuxclose(Queue *q) { Dk *dp; | |
| 1990/1101 | int i; | |
| 1990/0312 | ||
| 1990/1101 | dp = WR(q)->ptr; if(dp == 0) return; | |
| 1991/1120 | dp->name[0] = 0; | |
| 1990/1101 | /* * disallow new dkstopens() on this line. * the lock syncs with dkstopen(). */ lock(dp); dp->opened = 0; unlock(dp); /* * hang up all datakit connections */ for(i=dp->ncsc; i < dp->lines; i++) | |
| 1991/1107 | dkhangup(dp->linep[i]); | |
| 1990/1101 | /* * wakeup the timer so it can die */ wakeup(&dp->timer); | |
| 1990/0312 | } /* * handle configuration */ static void dkmuxoput(Queue *q, Block *bp) { if(bp->type != M_DATA){ if(streamparse("config", bp)) | |
| 1990/1101 | dkmuxconfig(q, bp); | |
| 1990/0312 | else PUTNEXT(q, bp); return; } PUTNEXT(q, bp); } /* * gather a message and send it up the appropriate stream * * The first two bytes of each message contains the channel * number, low order byte first. * * Simplifying assumption: one put == one message && the channel number * is in the first block. If this isn't true, demultiplexing will not | |
| 1990/1101 | * work. | |
| 1990/0312 | */ static void dkmuxiput(Queue *q, Block *bp) { Dk *dp; Line *lp; int line; | |
| 1990/1101 | /* * not configured yet */ if(q->other->ptr == 0){ freeb(bp); return; } | |
| 1990/0312 | dp = (Dk *)q->ptr; if(bp->type != M_DATA){ PUTNEXT(q, bp); return; } | |
| 1990/0315 | line = bp->rptr[0] | (bp->rptr[1]<<8); bp->rptr += 2; | |
| 1990/0312 | if(line<0 || line>=dp->lines){ | |
| 1990/0315 | DPRINT("dkmuxiput bad line %d\n", line); | |
| 1990/0312 | freeb(bp); return; } | |
| 1991/1107 | lp = dp->linep[line]; | |
| 1990/0312 | if(canqlock(lp)){ if(lp->rq) PUTNEXT(lp->rq, bp); else{ | |
| 1990/0315 | DPRINT("dkmuxiput unopened line %d\n", line); | |
| 1990/0312 | freeb(bp); } qunlock(lp); } else { | |
| 1990/0315 | DPRINT("dkmuxiput unopened line %d\n", line); | |
| 1990/0312 | freeb(bp); } } /* * the datakit line stream module definition */ static void dkstopen(Queue *, Stream *); static void dkstclose(Queue *); static void dkoput(Queue *, Block *); static void dkiput(Queue *, Block *); | |
| 1990/1113 | Qinfo dkinfo = { dkiput, dkoput, dkstopen, dkstclose, "dk" }; | |
| 1990/0312 | /* * open and save a pointer to the conversation */ static void dkstopen(Queue *q, Stream *s) { Dk *dp; Line *lp; dp = &dk[s->dev]; | |
| 1991/1107 | q->other->ptr = q->ptr = lp = dp->linep[s->id]; | |
| 1990/0312 | lp->dp = dp; | |
| 1990/1101 | lock(dp); | |
| 1991/1120 | dp->ref++; | |
| 1990/1101 | if(dp->opened==0 || streamenter(dp->s)<0){ unlock(dp); | |
| 1990/11211 | error(Ehungup); | |
| 1990/1101 | } unlock(dp); | |
| 1990/1104 | lp->rq = q; | |
| 1990/1101 | if(lp->state==Lclosed) | |
| 1990/0511 | lp->state = Lopened; | |
| 1990/0312 | } /* * close down a datakit conversation */ static void dkstclose(Queue *q) { Dk *dp; Line *lp; | |
| 1990/1101 | Chan *c; | |
| 1990/0312 | lp = (Line *)q->ptr; dp = lp->dp; /* | |
| 1990/1101 | * if we never got going, we're done | |
| 1990/0312 | */ | |
| 1990/1101 | if(lp->rq == 0){ lp->state = Lclosed; return; } /* * decrement ref count on mux'd line */ streamexit(dp->s, 0); /* * these states don't need the datakit */ | |
| 1990/0312 | switch(lp->state){ case Lclosed: case Llclose: | |
| 1990/1101 | case Lopened: lp->state = Lclosed; goto out; } | |
| 1990/0312 | ||
| 1990/1101 | c = 0; if(waserror()){ lp->state = Lclosed; if(c) close(c); goto out; } | |
| 1991/1116 | c = dkopencsc(dp); | |
| 1990/1101 | /* * shake hands with dk */ switch(lp->state){ | |
| 1990/0312 | case Lrclose: | |
| 1991/1107 | dkmesg(c, T_CHG, D_CLOSE, lp->lineno, 0); | |
| 1990/0312 | lp->state = Lclosed; break; case Lackwait: | |
| 1991/1107 | dkmesg(c, T_CHG, D_CLOSE, lp->lineno, 0); | |
| 1990/0312 | lp->state = Llclose; break; case Llistening: | |
| 1991/1107 | dkmesg(c, T_CHG, D_CLOSE, lp->lineno, 0); | |
| 1990/0312 | lp->state = Llclose; break; case Lconnected: | |
| 1991/1107 | dkmesg(c, T_CHG, D_CLOSE, lp->lineno, 0); | |
| 1990/0312 | lp->state = Llclose; break; } | |
| 1990/1101 | poperror(); close(c); | |
| 1990/0312 | ||
| 1990/1101 | out: | |
| 1990/0312 | qlock(lp); lp->rq = 0; qunlock(lp); | |
| 1991/1116 | if(lp->lineno == dp->ncsc) dp->csc = 0; netdisown(&dp->net, lp->lineno); | |
| 1991/1120 | lock(dp); dp->ref--; if(dp->ref == 0) freeb(dp->alloc); unlock(dp); | |
| 1990/0312 | } /* * this is only called by hangup */ static void dkiput(Queue *q, Block *bp) { PUTNEXT(q, bp); } /* * we assume that each put is a message. * | |
| 1990/0911 | * add a 2 byte channel number to the start of each message, * low order byte first. | |
| 1990/0312 | */ static void dkoput(Queue *q, Block *bp) { Line *lp; Dk *dp; int line; if(bp->type != M_DATA){ freeb(bp); | |
| 1990/11211 | error(Ebadarg); | |
| 1990/0312 | } lp = (Line *)q->ptr; dp = lp->dp; | |
| 1991/1107 | line = lp->lineno; | |
| 1990/0312 | ||
| 1990/0911 | bp = padb(bp, 2); | |
| 1990/0312 | bp->rptr[0] = line; bp->rptr[1] = line>>8; | |
| 1990/1018 | FLOWCTL(dp->wq); PUTNEXT(dp->wq, bp); | |
| 1990/0312 | } /* | |
| 1990/0911 | * configure a datakit multiplexor. this takes 5 arguments separated | |
| 1990/0312 | * by spaces: * the line number of the common signalling channel (must be > 0) * the number of lines in the device (optional) | |
| 1990/0707 | * the word `restart' or `norestart' (optional/default==restart) | |
| 1990/0911 | * the name of the dk (default==dk) * the urp window size (default==WS_2K) | |
| 1990/0312 | * * we can configure only once */ | |
| 1990/1202 | static int | |
| 1990/1210 | haveca(void *arg) | |
| 1990/1202 | { Dk *dp; dp = arg; | |
| 1990/1210 | return dp->closeall; | |
| 1990/1202 | } | |
| 1990/0312 | static void | |
| 1990/1101 | dkmuxconfig(Queue *q, Block *bp) | |
| 1990/0312 | { | |
| 1990/1101 | Dk *dp; | |
| 1990/0911 | char *fields[5]; | |
| 1990/0312 | int n; char buf[64]; | |
| 1990/1101 | char name[NAMELEN]; int lines; int ncsc; int restart; int window; | |
| 1990/0312 | ||
| 1990/1101 | if(WR(q)->ptr){ | |
| 1990/0312 | freeb(bp); | |
| 1990/11211 | error(Egreg); | |
| 1990/0312 | } /* | |
| 1990/1101 | * defaults */ ncsc = 1; restart = 1; lines = 16; window = WS_2K; strcpy(name, "dk"); /* | |
| 1990/0312 | * parse */ | |
| 1990/0911 | n = getfields((char *)bp->rptr, fields, 5, ' '); | |
| 1990/0312 | switch(n){ | |
| 1990/0911 | case 5: | |
| 1990/1101 | window = strtoul(fields[4], 0, 0); | |
| 1990/0707 | case 4: | |
| 1990/1101 | strncpy(name, fields[3], sizeof(name)); | |
| 1990/0312 | case 3: | |
| 1990/0707 | if(strcmp(fields[2], "restart")!=0) | |
| 1990/1101 | restart = 0; | |
| 1990/0312 | case 2: | |
| 1990/1101 | lines = strtoul(fields[1], 0, 0); | |
| 1990/0312 | case 1: | |
| 1990/1101 | ncsc = strtoul(fields[0], 0, 0); | |
| 1990/0312 | break; default: freeb(bp); | |
| 1990/11211 | error(Ebadarg); | |
| 1990/0312 | } freeb(bp); | |
| 1990/1101 | if(ncsc <= 0 || lines <= ncsc) | |
| 1990/11211 | error(Ebadarg); | |
| 1990/0312 | /* | |
| 1990/1101 | * set up | |
| 1990/0312 | */ | |
| 1991/1107 | dp = dkalloc(name, ncsc, lines); | |
| 1990/1101 | lock(dp); if(dp->opened){ unlock(dp); | |
| 1990/11211 | error(Ebadarg); | |
| 1990/0312 | } | |
| 1990/1101 | dp->restart = restart; dp->urpwindow = window; dp->s = RD(q)->ptr; q->ptr = q->other->ptr = dp; dp->opened = 1; dp->wq = WR(q); unlock(dp); | |
| 1990/0312 | /* | |
| 1990/1101 | * open csc here so that boot, dktimer, and dkcsckproc aren't * all fighting for it at once. */ | |
| 1990/1210 | dp->closeall = 0; | |
| 1991/1116 | dkopencsc(dp); | |
| 1990/1101 | /* | |
| 1990/1202 | * start a process to listen to csc messages */ sprint(buf, "csc.%s.%d", dp->name, dp->ncsc); kproc(buf, dkcsckproc, dp); | |
| 1990/1210 | tsleep(&dp->closeallr, haveca, dp, 5000); /* wait for initial closeall */ | |
| 1990/1202 | /* | |
| 1990/1004 | * tell datakit we've rebooted. It should close all channels. | |
| 1990/1101 | * do this here to get it done before trying to open a channel. | |
| 1990/1004 | */ if(dp->restart) { | |
| 1990/1101 | DPRINT("dktimer: restart %s\n", dp->name); | |
| 1990/1210 | dp->closeall = 0; | |
| 1990/1101 | dkmesg(dp->csc, T_ALIVE, D_RESTART, 0, 0); | |
| 1990/1210 | tsleep(&dp->closeallr, haveca, dp, 5000); /* wait for restart closeall */ | |
| 1990/1004 | } /* | |
| 1990/1101 | * start a keepalive process | |
| 1990/0312 | */ | |
| 1990/1101 | sprint(buf, "timer.%s.%d", dp->name, dp->ncsc); kproc(buf, dktimer, dp); | |
| 1990/0312 | } void dkreset(void) { | |
| 1991/1115 | int i; | |
| 1991/1107 | dk = (Dk*)ialloc(conf.dkif*sizeof(Dk), 0); | |
| 1991/1115 | for(i = 0; i < conf.dkif; i++) dk[i].prot = (Netprot*)ialloc(conf.nurp*sizeof(Netprot), 0); | |
| 1990/0321 | newqinfo(&dkmuxinfo); | |
| 1990/0312 | } void dkinit(void) { } Chan* dkattach(char *spec) { Chan *c; Dk *dp; /* | |
| 1990/0722 | * find a multiplexor with the same name (default dk) | |
| 1990/0312 | */ | |
| 1990/0722 | if(*spec == 0) spec = "dk"; | |
| 1991/1107 | dp = dkalloc(spec, 0, 0); | |
| 1990/1101 | /* * return the new channel */ | |
| 1990/0312 | c = devattach('k', spec); c->dev = dp - dk; return c; } Chan* dkclone(Chan *c, Chan *nc) { return devclone(c, nc); } int dkwalk(Chan *c, char *name) { | |
| 1991/1108 | return netwalk(c, name, &dk[c->dev].net); | |
| 1990/0312 | } void dkstat(Chan *c, char *dp) { | |
| 1991/1108 | netstat(c, dp, &dk[c->dev].net); | |
| 1990/0312 | } Chan* dkopen(Chan *c, int omode) { | |
| 1991/1108 | return netopen(c, omode, &dk[c->dev].net); | |
| 1990/0312 | } void dkcreate(Chan *c, char *name, int omode, ulong perm) { | |
| 1990/11211 | error(Eperm); | |
| 1990/0312 | } void dkclose(Chan *c) { | |
| 1990/0319 | if(c->stream) | |
| 1990/0312 | streamclose(c); } long | |
| 1991/0411 | dkread(Chan *c, void *a, long n, ulong offset) | |
| 1990/0312 | { | |
| 1991/1108 | return netread(c, a, n, offset, &dk[c->dev].net); | |
| 1990/0312 | } long | |
| 1991/0411 | dkwrite(Chan *c, void *a, long n, ulong offset) | |
| 1990/0312 | { int t; char buf[256]; char *field[5]; int m; | |
| 1990/11211 | t = STREAMTYPE(c->qid.path); | |
| 1990/0312 | /* * get data dispatched as quickly as possible */ if(t == Sdataqid) return streamwrite(c, a, n, 0); /* * easier to do here than in dkoput */ if(t == Sctlqid){ strncpy(buf, a, sizeof buf); m = getfields(buf, field, 5, ' '); if(strcmp(field[0], "connect")==0){ if(m < 2) | |
| 1990/11211 | error(Ebadarg); | |
| 1990/0312 | dkcall(Dial, c, field[1], 0, 0); } else if(strcmp(field[0], "announce")==0){ if(m < 2) | |
| 1990/11211 | error(Ebadarg); | |
| 1990/0312 | dkcall(Announce, c, field[1], 0, 0); } else if(strcmp(field[0], "redial")==0){ if(m < 4) | |
| 1990/11211 | error(Ebadarg); | |
| 1990/0312 | dkcall(Redial, c, field[1], field[2], field[3]); } else if(strcmp(field[0], "accept")==0){ if(m < 2) | |
| 1990/11211 | error(Ebadarg); | |
| 1990/0312 | dkanswer(c, strtoul(field[1], 0, 0), 0); } else if(strcmp(field[0], "reject")==0){ if(m < 3) | |
| 1990/11211 | error(Ebadarg); | |
| 1991/1206 | for(m = 0; m < DKERRS-1; m++) if(strcmp(field[2], dkerr[m]) == 0) break; dkanswer(c, strtoul(field[1], 0, 0), m); | |
| 1990/0312 | } else return streamwrite(c, a, n, 0); return n; } | |
| 1990/11211 | error(Eperm); | |
| 1990/0312 | } void dkremove(Chan *c) { | |
| 1990/11211 | error(Eperm); | |
| 1990/0312 | } void dkwstat(Chan *c, char *dp) { | |
| 1991/1115 | netwstat(c, dp, &dk[c->dev].net); | |
| 1990/0312 | } /* | |
| 1991/1108 | * return the number of an unused line (reserve it) */ static int dkcloneline(Chan *c) { Line *lp; Dk *dp; int line; dp = &dk[c->dev]; /* * get an unused device and open its control file */ for(line = dp->ncsc+1; line < dp->lines; line++){ lp = dp->linep[line]; if(lp->state == Lclosed && canqlock(lp)){ if(lp->state != Lclosed){ qunlock(lp); continue; } lp->state = Lopened; | |
| 1991/1115 | /* current user becomes owner */ netown(&dp->net, lp->lineno, u->p->user, 0); | |
| 1991/1108 | qunlock(lp); return lp->lineno; } } error(Enodev); } | |
| 1990/1101 | static Chan* dkopenline(Dk *dp, int line) { Chan *c; c = 0; if(waserror()){ if(c) close(c); nexterror(); } c = dkattach(dp->name); | |
| 1990/11211 | c->qid.path = STREAMQID(line, Sdataqid); | |
| 1990/1101 | dkopen(c, ORDWR); poperror(); return c; } /* | |
| 1991/1116 | * open the common signalling channel */ static Chan* dkopencsc(Dk *dp) { qlock(dp); if(dp->csc == 0) dp->csc = dkopenline(dp, dp->ncsc); else incref(dp->csc); qunlock(dp); return dp->csc; } /* | |
| 1991/1108 | * return the contents of the info files */ void dkfilladdr(Chan *c, char *buf, int len) { strncpy(buf, dk[c->dev].linep[STREAMID(c->qid.path)]->addr, len); } void dkfillraddr(Chan *c, char *buf, int len) { strncpy(buf, dk[c->dev].linep[STREAMID(c->qid.path)]->raddr, len); } void dkfillruser(Chan *c, char *buf, int len) { strncpy(buf, dk[c->dev].linep[STREAMID(c->qid.path)]->ruser, len); } /* | |
| 1990/0312 | * send a message to the datakit on the common signaling line */ static int | |
| 1990/1101 | dkmesg(Chan *c, int type, int srv, int p0, int p1) | |
| 1990/0312 | { Dkmsg d; | |
| 1990/0911 | if(waserror()){ print("dkmesg: error\n"); | |
| 1990/0312 | return -1; | |
| 1990/0911 | } | |
| 1990/0312 | d.type = type; d.srv = srv; d.param0l = p0; d.param0h = p0>>8; d.param1l = p1; d.param1h = p1>>8; d.param2l = 0; d.param2h = 0; d.param3l = 0; d.param3h = 0; d.param4l = 0; d.param4h = 0; | |
| 1990/1101 | streamwrite(c, (char *)&d, sizeof(Dkmsg), 1); | |
| 1990/0312 | poperror(); return 0; } /* * call out on a datakit */ static int calldone(void *a) { Line *lp; lp = (Line *)a; return lp->state != Ldialing; } static void dkcall(int type, Chan *c, char *addr, char *nuser, char *machine) { char dialstr[66]; int line; char dialtone; int t_val, d_val; Dk *dp; Line *lp; Chan *dc; | |
| 1990/1101 | Chan *csc; | |
| 1990/0707 | char *bang, *dot; | |
| 1990/1101 | ||
| 1990/11211 | line = STREAMID(c->qid.path); | |
| 1990/0312 | dp = &dk[c->dev]; | |
| 1991/1107 | lp = dp->linep[line]; | |
| 1990/0312 | /* * only dial on virgin lines */ | |
| 1990/0511 | if(lp->state != Lopened) | |
| 1990/11211 | error(Ebadarg); | |
| 1990/0312 | DPRINT("dkcall(line=%d, type=%d, dest=%s)\n", line, type, addr); /* | |
| 1990/0707 | * build dial string * - guard against new lines * - change ! into . to delimit service | |
| 1990/0312 | */ if(strchr(addr, '\n')) | |
| 1990/11211 | error(Ebadarg); | |
| 1991/1105 | if(strlen(addr)+strlen(u->p->user)+2 >= sizeof(dialstr)) | |
| 1990/11211 | error(Ebadarg); | |
| 1990/0312 | strcpy(dialstr, addr); | |
| 1990/0707 | bang = strchr(dialstr, '!'); if(bang){ dot = strchr(dialstr, '.'); if(dot==0 || dot > bang) *bang = '.'; } | |
| 1990/0312 | switch(type){ case Dial: t_val = T_SRV; d_val = D_DIAL; strcat(dialstr, "\n"); | |
| 1991/1105 | strcat(dialstr, u->p->user); | |
| 1990/0312 | strcat(dialstr, "\n"); break; case Announce: t_val = T_SRV; d_val = D_SERV; break; case Redial: t_val = T_CHG; d_val = D_REDIAL; strcat(dialstr, "\n"); strcat(dialstr, nuser); strcat(dialstr, "\n"); strcat(dialstr, machine); strcat(dialstr, "\n"); break; | |
| 1990/1214 | default: t_val = 0; d_val = 0; panic("bad dial type"); | |
| 1990/0312 | } /* | |
| 1990/1214 | * open the data file | |
| 1990/0312 | */ | |
| 1990/1214 | dc = dkopenline(dp, line); | |
| 1990/0312 | if(waserror()){ | |
| 1990/1214 | close(dc); | |
| 1990/0312 | nexterror(); } lp->calltolive = 4; lp->state = Ldialing; /* * tell the controller we want to make a call */ DPRINT("dialout\n"); | |
| 1991/1116 | csc = dkopencsc(dp); | |
| 1990/1214 | if(waserror()){ close(csc); nexterror(); } | |
| 1990/1101 | dkmesg(csc, t_val, d_val, line, W_WINDOW(dp->urpwindow,dp->urpwindow,2)); | |
| 1990/1214 | poperror(); | |
| 1990/1101 | close(csc); | |
| 1990/0312 | /* * if redial, wait for a dial tone (otherwise we might send * the dialstr to the previous other end and not the controller) */ if(type==Redial){ if(streamread(dc, &dialtone, 1L) != 1L){ lp->state = Lconnected; | |
| 1990/11211 | error(Ebadarg); | |
| 1990/0312 | } } /* * make the call */ DPRINT("dialstr %s\n", dialstr); streamwrite(dc, dialstr, (long)strlen(dialstr), 1); close(dc); poperror(); /* * redial's never get a reply, assume it worked */ if(type == Redial) { lp->state = Lconnected; return; } /* * wait for a reply */ DPRINT("reply wait\n"); sleep(&lp->r, calldone, lp); /* * if there was an error, translate it to a plan 9 * errno and report it to the user. */ DPRINT("got reply %d\n", lp->state); if(lp->state != Lconnected) { if(lp->err >= DKERRS) | |
| 1991/1206 | errors(dkerr[0]); | |
| 1990/0312 | else | |
| 1991/1206 | errors(dkerr[lp->err]); | |
| 1990/0312 | } /* | |
| 1990/0617 | * change state if serving | |
| 1990/0312 | */ if(type == D_SERV){ lp->state = Llistening; } DPRINT("connected!\n"); /* * decode the window size */ if (W_VALID(lp->window)){ /* * a 1127 window negotiation */ lp->window = W_VALUE(W_DEST(lp->window)); } else if(lp->window>2 && lp->window<31){ /* * a generic window negotiation */ lp->window = 1<<lp->window; } else lp->window = 0; /* * tag the connection */ strncpy(lp->addr, addr, sizeof(lp->addr)-1); strncpy(lp->raddr, addr, sizeof(lp->raddr)-1); /* * reset the protocol */ dkwindow(c); } /* * listen for a call, reflavor the */ static int dklisten(Chan *c) { char dialstr[512]; char *line[12]; char *field[8]; Line *lp; Dk *dp; int n, lineno, ts, window; | |
| 1991/1115 | int from; | |
| 1990/0312 | Chan *dc; dp = &dk[c->dev]; | |
| 1991/1115 | from = STREAMID(c->qid.path); | |
| 1990/0312 | /* * open the data file */ | |
| 1990/11211 | dc = dkopenline(dp, STREAMID(c->qid.path)); | |
| 1990/0312 | if(waserror()){ close(dc); nexterror(); } /* * wait for a call in */ for(;;){ /* * read the dialstring and null terminate it */ n = streamread(dc, dialstr, sizeof(dialstr)-1); DPRINT("returns %d\n", n); if(n <= 0) | |
| 1990/11211 | error(Eio); | |
| 1990/0312 | dialstr[n] = 0; DPRINT("dialstr = %s\n", dialstr); /* * break the dial string into lines */ n = getfields(dialstr, line, 12, '\n'); if (n < 2) { DPRINT("bad dialstr from dk (1 line)\n"); | |
| 1990/11211 | error(Eio); | |
| 1990/0312 | } /* * line 0 is `line.tstamp.traffic[.urpparms.window]' */ window = 0; switch(getfields(line[0], field, 5, '.')){ case 5: /* * generic way of passing window */ window = strtoul(field[4], 0, 0); if(window > 0 && window <31) window = 1<<window; else window = 0; /* * intentional fall through */ case 3: /* * 1127 way of passing window */ if(window == 0){ window = strtoul(field[2], 0, 0); if(W_VALID(window)) window = W_VALUE(W_ORIG(window)); else window = 0; } break; default: print("bad message from dk(bad first line)\n"); continue; } lineno = strtoul(field[0], 0, 0); if(lineno >= dp->lines){ print("dklisten: illegal line %d\n", lineno); continue; } | |
| 1991/1107 | lp = dp->linep[lineno]; | |
| 1990/0312 | ts = strtoul(field[1], 0, 0); /* * this could be a duplicate request */ if(ts == lp->timestamp){ print("dklisten: repeat timestamp %d\n", lineno); | |
| 1990/0403 | if(lp->state != Lconnected) dkanswer(c, lineno, DKbusy); | |
| 1990/0312 | continue; } /* * take care of glare (datakit picked an inuse channel * for the call to come in on). */ if(!canqlock(lp)){ | |
| 1990/0315 | DPRINT("DKbusy1\n"); | |
| 1990/0312 | dkanswer(c, lineno, DKbusy); continue; } else { if(lp->state != Lclosed){ qunlock(lp); | |
| 1990/0315 | DPRINT("DKbusy2 %ux\n", lp->state); | |
| 1990/0312 | dkanswer(c, lineno, DKbusy); continue; } } lp->window = window; /* * Line 1 is `my-dk-name.service[.more-things]'. * Special characters are escaped by '\'s. */ strncpy(lp->addr, line[1], sizeof(lp->addr)-1); /* * the rest is variable length */ switch(n) { case 2: /* no more lines */ lp->ruser[0] = 0; lp->raddr[0] = 0; break; case 3: /* line 2 is `source.user.param1.param2' */ getfields(line[2], field, 3, '.'); strncpy(lp->raddr, field[0], sizeof(lp->raddr)-1); strncpy(lp->ruser, field[1], sizeof(lp->ruser)-1); break; case 4: /* line 2 is `user.param1.param2' */ getfields(line[2], field, 2, '.'); strncpy(lp->ruser, field[0], sizeof(lp->ruser)-1); /* line 3 is `source.node.mod.line' */ strncpy(lp->raddr, line[3], sizeof(lp->raddr)-1); break; default: print("bad message from dk(>4 line)\n"); qunlock(lp); | |
| 1990/11211 | error(Ebadarg); | |
| 1990/0312 | } | |
| 1991/1107 | DPRINT("src(%s)user(%s)dest(%s)w(%d)\n", lp->raddr, lp->ruser, lp->addr, W_TRAF(lp->window)); | |
| 1990/0312 | lp->timestamp = ts; lp->state = Lconnected; | |
| 1991/1115 | /* listener becomes owner */ netown(&dp->net, lp->lineno, dp->prot[from].owner, 0); | |
| 1990/0312 | qunlock(lp); close(dc); poperror(); DPRINT("dklisten returns %d\n", lineno); return lineno; } | |
| 1990/0511 | panic("dklisten terminates strangely\n"); | |
| 1990/0312 | } /* * answer a call */ static void dkanswer(Chan *c, int line, int code) { char reply[64]; Dk *dp; Chan *dc; Line *lp; dp = &dk[c->dev]; | |
| 1991/1107 | lp = dp->linep[line]; | |
| 1990/0312 | /* * open the data file (c is a control file) */ dc = dkattach(dp->name); if(waserror()){ close(dc); nexterror(); } | |
| 1990/11211 | dc->qid.path = STREAMQID(STREAMID(c->qid.path), Sdataqid); | |
| 1990/0312 | dkopen(dc, ORDWR); /* * send the reply */ sprint(reply, "%ud.%ud.%ud", line, lp->timestamp, code); DPRINT("dkanswer %s\n", reply); streamwrite(dc, reply, strlen(reply), 1); close(dc); poperror(); } /* * set the window size and reset the protocol */ static void dkwindow(Chan *c) { char buf[64]; long wins; Line *lp; | |
| 1991/1107 | lp = dk[c->dev].linep[STREAMID(c->qid.path)]; | |
| 1990/0312 | if(lp->window == 0) lp->window = 64; sprint(buf, "init %d %d", lp->window, Streamhi); streamwrite(c, buf, strlen(buf), 1); } /* * hangup a datakit connection */ static void dkhangup(Line *lp) { Block *bp; qlock(lp); if(lp->rq){ bp = allocb(0); bp->type = M_HANGUP; PUTNEXT(lp->rq, bp); } qunlock(lp); } /* * A process which listens to all input on a csc line */ static void dkcsckproc(void *a) { long n; Dk *dp; Dkmsg d; int line; | |
| 1990/1101 | dp = a; | |
| 1990/0312 | ||
| 1990/1101 | if(waserror()){ close(dp->csc); return; } | |
| 1991/1108 | DPRINT("dkcsckproc: %d\n", dp->ncsc); | |
| 1990/1101 | ||
| 1990/0312 | /* * loop forever listening */ for(;;){ n = streamread(dp->csc, (char *)&d, (long)sizeof(d)); if(n != sizeof(d)){ | |
| 1990/1101 | if(n == 0) | |
| 1990/11211 | error(Ehungup); | |
| 1990/0312 | print("strange csc message %d\n", n); continue; } line = (d.param0h<<8) + d.param0l; | |
| 1991/1108 | DPRINT("t(%d)s(%d)l(%d)\n", d.type, d.srv, line); | |
| 1990/0312 | switch (d.type) { case T_CHG: /* controller wants to close a line */ | |
| 1990/1101 | dkchgmesg(dp->csc, dp, &d, line); | |
| 1990/0312 | break; case T_REPLY: /* reply to a dial request */ dkreplymesg(dp, &d, line); break; case T_SRV: /* ignore it, it's useless */ | |
| 1991/0723 | /* print("dksrvmesg(%d)\n", line); /**/ | |
| 1990/0312 | break; case T_RESTART: /* datakit reboot */ | |
| 1990/1101 | if(line >=0 && line<dp->lines) | |
| 1991/0828 | dp->lines = line+1; | |
| 1990/0312 | break; default: | |
| 1991/0706 | print("unrecognized csc message %o.%o(%o)\n", d.type, d.srv, line); | |
| 1990/0312 | break; } } } /* * datakit requests or confirms closing a line */ static void | |
| 1990/1101 | dkchgmesg(Chan *c, Dk *dp, Dkmsg *dialp, int line) | |
| 1990/0312 | { Line *lp; switch (dialp->srv) { case D_CLOSE: /* remote shutdown */ | |
| 1990/1022 | if (line <= 0 || line >= dp->lines) { /* tell controller this line is not in use */ | |
| 1990/1101 | dkmesg(c, T_CHG, D_CLOSE, line, 0); | |
| 1990/1022 | return; } | |
| 1991/1107 | lp = dp->linep[line]; | |
| 1990/0312 | switch (lp->state) { case Ldialing: /* simulate a failed connection */ dkreplymesg(dp, (Dkmsg *)0, line); lp->state = Lrclose; break; case Lrclose: case Lconnected: case Llistening: case Lackwait: dkhangup(lp); lp->state = Lrclose; break; case Lopened: | |
| 1990/1101 | dkmesg(c, T_CHG, D_CLOSE, line, 0); | |
| 1990/0312 | break; case Llclose: case Lclosed: dkhangup(lp); | |
| 1990/1101 | dkmesg(c, T_CHG, D_CLOSE, line, 0); | |
| 1990/0312 | lp->state = Lclosed; break; } break; case D_ISCLOSED: /* acknowledging a local shutdown */ | |
| 1990/1022 | if (line <= 0 || line >= dp->lines) { /* tell controller this line is not in use */ | |
| 1990/1101 | dkmesg(c, T_CHG, D_CLOSE, line, 0); | |
| 1990/1022 | return; } | |
| 1991/1107 | lp = dp->linep[line]; | |
| 1990/0312 | switch (lp->state) { case Llclose: case Lclosed: lp->state = Lclosed; break; case Lrclose: case Lconnected: case Llistening: case Lackwait: break; } break; | |
| 1990/1022 | case D_CLOSEALL: | |
| 1990/1101 | /* * datakit wants us to close all lines */ | |
| 1990/1022 | for(line = dp->ncsc+1; line < dp->lines; line++){ | |
| 1991/1107 | lp = dp->linep[line]; | |
| 1990/1022 | switch (lp->state) { case Ldialing: /* simulate a failed connection */ dkreplymesg(dp, (Dkmsg *)0, line); lp->state = Lrclose; break; case Lrclose: case Lconnected: case Llistening: case Lackwait: lp->state = Lrclose; | |
| 1990/1101 | dkhangup(lp); | |
| 1990/1022 | break; case Lopened: break; case Llclose: case Lclosed: lp->state = Lclosed; break; } } | |
| 1990/1210 | dp->closeall = 1; wakeup(&dp->closeallr); | |
| 1990/1022 | break; | |
| 1990/0312 | default: print("unrecognized T_CHG\n"); } } /* * datakit replies to a dialout. capture reply code and traffic parameters */ static void dkreplymesg(Dk *dp, Dkmsg *dialp, int line) { Proc *p; Line *lp; DPRINT("dkreplymesg(%d)\n", line); if(line < 0 || line >= dp->lines) return; | |
| 1991/1107 | lp = dp->linep[line]; | |
| 1990/0312 | if(lp->state != Ldialing) return; if(dialp){ /* * a reply from the dk */ lp->state = (dialp->srv==D_OPEN) ? Lconnected : Lrclose; lp->err = (dialp->param1h<<8) + dialp->param1l; lp->window = lp->err; DPRINT("dkreplymesg: %d\n", lp->state); } else { /* * a local abort */ lp->state = Lrclose; lp->err = 0; } if(lp->state==Lrclose){ dkhangup(lp); } wakeup(&lp->r); } /* | |
| 1990/1101 | * send a I'm alive message every 7.5 seconds and remind the dk of * any closed channels it hasn't acknowledged. | |
| 1990/0312 | */ static void dktimer(void *a) { int dki, i; Dk *dp; Line *lp; | |
| 1990/1101 | Chan *c; | |
| 1990/0312 | ||
| 1990/1214 | dp = (Dk *)a; | |
| 1990/1101 | c = 0; if(waserror()){ /* * hang up any calls waiting for the dk */ for (i=dp->ncsc+1; i<dp->lines; i++){ | |
| 1991/1107 | lp = dp->linep[i]; | |
| 1990/1101 | switch(lp->state){ case Llclose: lp->state = Lclosed; break; | |
| 1990/0312 | ||
| 1990/1101 | case Ldialing: dkreplymesg(dp, (Dkmsg *)0, i); break; } } if(c) close(c); return; } | |
| 1991/1116 | c = dkopencsc(dp); | |
| 1990/1101 | ||
| 1990/0312 | for(;;){ | |
| 1990/1101 | if(dp->opened==0) | |
| 1990/11211 | error(Ehungup); | |
| 1990/1101 | ||
| 1990/0312 | /* | |
| 1990/1101 | * send keep alive | |
| 1990/0312 | */ | |
| 1990/11161 | DPRINT("keep alive\n"); | |
| 1990/1101 | dkmesg(c, T_ALIVE, D_CONTINUE, 0, 0); | |
| 1990/0312 | ||
| 1990/1101 | /* * remind controller of dead lines and * timeout calls that take to long */ for (i=dp->ncsc+1; i<dp->lines; i++){ | |
| 1991/1107 | lp = dp->linep[i]; | |
| 1990/1101 | switch(lp->state){ case Llclose: dkmesg(c, T_CHG, D_CLOSE, i, 0); break; | |
| 1990/0312 | ||
| 1990/1101 | case Ldialing: if(lp->calltolive==0 || --lp->calltolive!=0) | |
| 1990/0312 | break; | |
| 1990/1101 | dkreplymesg(dp, (Dkmsg *)0, i); break; | |
| 1990/0312 | } } | |
| 1990/1101 | tsleep(&dp->timer, return0, 0, 7500); | |
| 1990/0312 | } } | |