6 #define LOGFILE "telco"
9 * Rather than reading /adm/users, which is a lot of work for
10 * a toy progdev, we assume all groups have the form
12 * meaning that each user is the leader of his own group.
17 OPERM = 0x3, /* mask of all permission types in open mode */
23 typedef struct Fid Fid;
24 typedef struct Dev Dev;
25 typedef struct Request Request;
26 typedef struct Type Type;
53 int ctl; /* control fd */
54 int data; /* data fd */
55 char *path; /* to device */
71 int monitoring; /* monitor pid */
107 #define DEV(q) ((((ulong)(q).path)&Devmask)>>8)
108 #define TYPE(q) (((ulong)(q).path)&((1<<8)-1))
109 #define MKQID(t, i) ((((i)<<8)&Devmask) | (t))
114 * modem specific commands
116 Cerrorcorrection = 0, /* error correction */
117 Ccompression, /* compression */
118 Cflowctl, /* CTS/RTS */
119 Crateadjust, /* follow line speed */
120 Cfclass2, /* set up for fax */
121 Cfclass0, /* set up for data */
128 char *ident; /* inquire request */
129 char *response; /* inquire response (strstr) */
130 char *basetype; /* name of base type */
132 char *commands[Ncommand];
138 * FCLASS=2 - set to service class 2, i.e., one where the fax handles timing
141 * FCQ=1 - receive copy quality checking enabled
142 * FBOR=1 - set reversed bit order for phase C data
143 * FCR=1 - the DCE can receive message data, bit 10 in the DIS or
144 * DTC frame will be set
145 * FDIS=,3 - limit com speed to 9600 baud
150 { "Rockwell", 0, 0, 0,
151 "AT\\N7", /* auto reliable (V.42, fall back to MNP, to none) */
152 "AT%C1\\J0", /* negotiate for compression, don't change port baud rate */
153 "AT\\Q3", /* CTS/RTS flow control */
155 "AT+FCLASS=2\rAT+FCR=1\r",
159 { "ATT2400", "ATI9", "E2400", "Rockwell",
160 "AT\\N3", /* auto reliable (MNP, fall back to none) */
168 { "ATT14400", "ATI9", "E14400", "Rockwell",
177 { "MT1432", "ATI2", "MT1432", 0,
178 "AT&E1", /* auto reliable (V.42, fall back to none) */
179 "AT&E15$BA0", /* negotiate for compression */
180 "AT&E4", /* CTS/RTS flow control */
181 "AT$BA1", /* don't change port baud rate */
182 "AT+FCLASS=2\rAT+FTBC=0\rAT+FREL=1\rAT+FCQ=1\rAT+FBOR=1\rAT+FCR=1\rAT+FDIS=,3",
186 { "MT2834", "ATI2", "MT2834", "MT1432",
191 "AT+FCLASS=2\rAT+FTBC=0\rAT+FREL=1\rAT+FCQ=1\rAT+FBOR=1\rAT+FCR=1",
195 { "VOCAL", "ATI6", "144DPL+FAX", "Rockwell",
196 "AT\\N3", /* auto reliable (V.42, fall back to MNP, fall back to none) */
197 "AT%C3\\J0", /* negotiate for compression, don't change port baud rate */
200 "AT+FCLASS=2\rAT+FTBC=0\rAT+FREL=1\rAT+FCQ=1\rAT+FBOR=1\rAT+FCR=1",
220 * modem return messages
222 typedef struct Msg Msg;
232 { "NO CARRIER", Failure, },
233 { "ERROR", Failure, },
234 { "NO DIALTONE", Failure, },
235 { "BUSY", Failure, },
236 { "NO ANSWER", Failure, },
237 { "CONNECT", Success, },
246 uchar mdata[8192+IOHDRSZ];
247 int messagesize = sizeof mdata;
251 uchar statbuf[STATMAX];
254 int maxspeed = 56000;
255 char *srcid = "plan9";
259 int devstat(Dir*, uchar*, int);
260 int devgen(Qid, int, Dir*, uchar*, int);
263 void *erealloc(void*, ulong);
264 void *emalloc(ulong);
266 int perm(Fid*, Dev*, int);
267 void setspeed(Dev*, int);
268 int getspeed(char*, int);
269 char *dialout(Dev*, char*);
271 int readmsg(Dev*, int, char*);
273 int getinput(Dev*, char*, int);
276 char* modemtype(Dev*, int, int);
279 char *rflush(Fid*), *rversion(Fid*),
280 *rattach(Fid*), *rauth(Fid*), *rwalk(Fid*),
281 *ropen(Fid*), *rcreate(Fid*),
282 *rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
283 *rremove(Fid*), *rstat(Fid*), *rwstat(Fid*);
285 char *(*fcalls[])(Fid*) = {
301 char Eperm[] = "permission denied";
302 char Enotdir[] = "not a directory";
303 char Enotexist[] = "file does not exist";
304 char Ebadaddr[] = "bad address";
305 char Eattn[] = "can't get modem's attention";
306 char Edial[] = "can't dial";
307 char Enoauth[] = "telco: authentication not required";
308 char Eisopen[] = "file already open for I/O";
309 char Enodev[] = "no free modems";
310 char Enostream[] = "stream closed prematurely";
315 fprint(2, "usage: %s [-vp] [-i srcid] dev ...\n", argv0);
320 notifyf(void *a, char *s)
323 if(strncmp(s, "interrupt", 9) == 0)
329 main(int argc, char *argv[])
347 maxspeed = atoi(ARGF());
362 error("pipe failed");
365 fmtinstall('F', fcallfmt);
368 switch(rfork(RFFDG|RFPROC|RFREND|RFNOTEG)){
373 mfd[0] = mfd[1] = p[0];
377 fd = create("/srv/telco", OWRITE, 0666);
379 error("create of /srv/telco failed");
380 sprint(buf, "%d", p[1]);
381 if(write(fd, buf, strlen(buf)) < 0)
382 error("writing /srv/telco");
384 if(mount(p[1], -1, "/net", MBEFORE, "") < 0)
385 error("mount failed");
389 dev = mallocz(argc*sizeof(Dev), 1);
390 for(ndev = 0; ndev < argc; ndev++){
392 d->path = argv[ndev];
393 d->rp = d->wp = d->rbuf;
404 * generate a stat structure for a qid
407 devstat(Dir *dir, uchar *buf, int nbuf)
411 static char tmp[10][32];
416 dir->name = names[t];
418 dir->name = tmp[ntmp % nelem(tmp)];
419 sprint(dir->name, "%lud", DEV(dir->qid));
427 d = &dev[DEV(dir->qid)];
433 if(dir->qid.type & QTDIR)
436 d = &dev[DEV(dir->qid)];
437 dir->length = d->wp - d->rp;
439 dir->length += Nrbuf;
442 dir->atime = time(0);
443 dir->mtime = dir->atime;
445 return convD2M(dir, buf, nbuf);
450 * enumerate file's we can walk to from q
453 devgen(Qid q, int i, Dir *d, uchar *buf, int nbuf)
472 d->qid.type = QTFILE;
473 d->qid.path = Qclone;
479 d->qid.path = MKQID(Qlvl3, i-1);
490 d->qid.type = QTFILE;
491 d->qid.path = MKQID(Qdata, DEV(q));
494 d->qid.type = QTFILE;
495 d->qid.path = MKQID(Qctl, DEV(q));
504 return devstat(d, buf, nbuf);
512 for(f = fids; f; f = f->next)
517 return "version: message size too small";
518 messagesize = thdr.msize;
519 if(messagesize > sizeof mdata)
520 messagesize = sizeof mdata;
521 rhdr.msize = messagesize;
522 if(strncmp(thdr.version, "9P2000", 6) != 0)
523 return "unrecognized 9P version";
524 rhdr.version = "9P2000";
535 for(d = dev; d < &dev[ndev]; d++){
537 for(l = &d->r; r = *l; l = &r->next)
538 if(r->tag == thdr.oldtag){
564 f->user = strdup(thdr.uname);
580 if(thdr.fid != thdr.newfid){
585 nf = newfid(thdr.newfid);
589 nf->user = strdup(f->user);
597 for(; nqid < thdr.nwname; nqid++) {
598 if((dir.qid.type & QTDIR) == 0){
602 name = thdr.wname[nqid];
603 if(strcmp(name, ".") == 0){
605 }else if(strcmp(name, "..") == 0) {
606 if(devgen(f->qid, -1, &dir, 0, 0) < 0)
612 if(devgen(q, i, &dir, 0, 0) < 0)
614 if(strcmp(name, dir.name) == 0)
618 rhdr.wqid[nqid] = dir.qid;
621 if(nqid == 0 && err == nil)
623 if(nf != nil && thdr.fid != thdr.newfid && nqid < thdr.nwname)
628 if(nqid > 0 && nqid == thdr.nwname)
643 if(f->qid.type & QTDIR){
653 for(d = dev; d < &dev[ndev]; d++)
658 f->qid.path = MKQID(Qctl, d-dev);
664 d = &dev[DEV(f->qid)];
666 d->user = strdup(f->user);
669 if(mode==OWRITE || mode==ORDWR)
670 if(!perm(f, d, Pwrite))
672 if(mode==OREAD || mode==ORDWR)
673 if(!perm(f, d, Pread))
680 rhdr.iounit = messagesize - IOHDRSZ;
696 takeanote(void *u, char *note)
699 if(strstr(note, "flushed"))
724 for(i = 0; n < cnt; i++){
725 m = devgen(f->qid, i, &dir, (uchar*)buf+n, cnt-n);
734 i = sprint(num, "%lud", DEV(f->qid));
739 memmove(buf, num + off, n);
744 d = &dev[DEV(f->qid)];
745 r = mallocz(sizeof(Request), 1);
747 r->count = thdr.count;
764 char *cmsg = "connect ";
778 switch(TYPE(f->qid)){
780 return "file is a directory";
782 d = &dev[DEV(f->qid)];
784 if(cnt < clen || strncmp(thdr.data, cmsg, clen) != 0){
786 * send control message to real control file
788 if(seek(d->ctl, off, 0) < 0 || write(d->ctl, thdr.data, cnt) < 0){
789 errstr(errbuf, sizeof errbuf);
797 if(cnt >= sizeof(buf))
798 cnt = sizeof(buf) - 1;
801 strncpy(buf, &thdr.data[clen], cnt);
803 cp = dialout(d, buf);
810 d = &dev[DEV(f->qid)];
811 if(write(d->data, thdr.data, cnt) < 0){
812 errstr(errbuf, sizeof errbuf);
827 switch(TYPE(f->qid)){
830 d = &dev[DEV(f->qid)];
856 rhdr.nstat = devstat(&d, statbuf, sizeof statbuf);
866 if(TYPE(f->qid) < Qlvl3)
869 convM2D(thdr.stat, thdr.nstat, &dir, rhdr.data); /* rhdr.data is a known place to scribble */
870 d = &dev[DEV(f->qid)];
873 * To change mode, must be owner
875 if(d->perm != dir.mode){
876 if(strcmp(f->user, d->user) != 0)
877 if(strcmp(f->user, user) != 0)
882 d->perm = dir.mode & ~DMDIR;
892 for(f = fids; f; f = f->next)
895 else if(!ff && !f->busy)
901 f = emalloc(sizeof *f);
909 * read fs requests and dispatch them
919 * reading from a pipe or a network device
920 * will give an error after a few eof reads
921 * however, we cannot tell the difference
922 * between a zero-length read and an interrupt
923 * on the processes writing to us,
924 * so we wait for the error
926 n = read9pmsg(mfd[0], mdata, messagesize);
931 if(convM2S(mdata, n, &thdr) != n)
932 error("convM2S error");
934 rhdr.data = (char*)mdata + IOHDRSZ;
935 if(!fcalls[thdr.type])
936 err = "bad fcall type";
938 err = (*fcalls[thdr.type])(newfid(thdr.fid));
941 continue; /* assigned to a slave */
945 rhdr.type = thdr.type + 1;
949 n = convS2M(&rhdr, mdata, messagesize);
950 if(write(mfd[1], mdata, n) != n)
951 error("mount write");
957 perm(Fid *f, Dev *d, int p)
959 if((p*Pother) & d->perm)
961 if(strcmp(f->user, user)==0 && ((p*Pgroup) & d->perm))
963 if(strcmp(f->user, d->user)==0 && ((p*Powner) & d->perm))
971 fprint(2, "%s: %s: %r\n", argv0, s);
972 syslog(0, LOGFILE, "%s: %r", s);
973 remove("/srv/telco");
974 postnote(PNGROUP, getpid(), "exit");
985 error("out of memory");
990 erealloc(void *p, ulong n)
994 error("out of memory");
999 * send bytes to modem
1002 send(Dev *d, char *x)
1005 syslog(0, LOGFILE, "->%s", x);
1006 return write(d->data, x, strlen(x));
1010 * apply a string of commands to modem
1013 apply(Dev *d, char *s, char *substr, int secs)
1023 if(c == '\r' || *s == 0){
1027 if(send(d, buf) < 0)
1029 m = readmsg(d, secs, substr);
1037 * apply a command type
1040 applyspecial(Dev *d, int index)
1044 cmd = d->t->commands[index];
1045 if(cmd == 0 && d->baset)
1046 cmd = d->baset->commands[index];
1050 return apply(d, cmd, 0, 2);
1054 * get modem into command mode if it isn't already
1061 for(i = 0; i < 2; i++){
1063 if(send(d, "+") < 0)
1066 if(send(d, "+") < 0)
1069 if(send(d, "+") < 0)
1073 if(apply(d, "ATZH0", 0, 2) == Ok)
1079 int portspeed[] = { 56000, 38400, 19200, 14400, 9600, 4800, 2400, 1200, 600, 300, 0 };
1082 * get the modem's type and speed
1085 modemtype(Dev *d, int limit, int fax)
1094 /* assume we're at a good speed, try getting attention a few times */
1097 /* find a common port rate */
1098 for(p = portspeed; *p; p++){
1102 if(attention(d) == Ok)
1109 syslog(0, LOGFILE, "port speed %d", *p);
1112 * basic Hayes commands everyone implements (we hope)
1113 * Q0 = report result codes
1114 * V1 = full word result codes
1115 * E0 = don't echo commands
1116 * M1 = speaker on until on-line
1117 * S0=0 = autoanswer off
1119 if(apply(d, "ATQ0V1E0M1S0=0", 0, 2) != Ok)
1122 /* find modem type */
1123 for(t = typetab; t->name; t++){
1124 if(t->ident == 0 || t->response == 0)
1126 if(apply(d, t->ident, t->response, 2) == Found)
1134 for(bt = typetab; bt->name; bt++)
1135 if(strcmp(bt->name, t->basetype) == 0)
1142 syslog(0, LOGFILE, "modem %s", d->t->name);
1144 /* try setting fax modes */
1147 /* set up fax parameters */
1148 if(applyspecial(d, Cfclass2) != Failure)
1151 /* setup a source id */
1153 sprint(buf, "AT+FLID=\"%s\"", srcid);
1154 apply(d, buf, 0, 2);
1157 /* allow both data and fax calls in */
1158 apply(d, "AT+FAA=1", 0, 2);
1160 applyspecial(d, Cfclass0);
1165 * a process to read input from a modem.
1176 d->ctl = d->data = -1;
1180 sprint(file, "%sctl", d->path);
1181 d->ctl = open(file, ORDWR);
1183 error("opening ctl");
1184 d->data = open(d->path, ORDWR);
1186 error("opening data");
1187 d->wp = d->rp = d->rbuf;
1192 switch(d->pid = rfork(RFPROC|RFMEM)){
1194 error("out of processes");
1202 /* wait for ring or off hook */
1203 while(d->open == 0){
1206 n = read(d->data, p, 1);
1209 if(p < &d->rbuf[Nrbuf] - 2)
1211 if(*p == '\r' || *p == '\n'){
1214 syslog(0, LOGFILE, "<:-%s", d->rp);
1215 if(answer && strncmp(d->rp, "RING", 4) == 0){
1224 /* shuttle bytes till on hook */
1227 n = &d->rbuf[Nrbuf] - d->wp;
1229 n = d->rp - d->wp - 1;
1231 n = read(d->data, d->wp, n);
1233 read(d->data, file, sizeof(file));
1239 if(d->wp + n >= &d->rbuf[Nrbuf])
1253 * get bytes input by monitor() (only routine that changes d->rp)
1256 getinput(Dev *d, char *buf, int n)
1266 i = &d->rbuf[Nrbuf] - d->rp;
1271 memmove(p, d->rp, i);
1272 if(d->rp + i == &d->rbuf[Nrbuf])
1283 * fulfill a read request (we assume d is locked)
1294 mdata = malloc(messagesize);
1295 buf = malloc(messagesize-IOHDRSZ);
1298 if(d->r == 0 || d->rp == d->wp)
1301 if(r->count > sizeof(buf))
1302 r->count = sizeof(buf);
1304 n = getinput(d, buf, r->count);
1310 rhdr.fid = r->fid->fid;
1314 n = convS2M(&rhdr, mdata, messagesize);
1315 if(write(mfd[1], mdata, n) != n)
1316 fprint(2, "telco: error writing\n");
1327 dialout(Dev *d, char *number)
1329 int i, m, compress, rateadjust, speed, fax;
1335 rateadjust = Failure;
1339 m = getfields(number, field, 5, 1, "!");
1340 for(i = 1; i < m; i++){
1341 if(field[i][0] >= '0' && field[i][0] <= '9')
1342 speed = atoi(field[i]);
1343 else if(strcmp(field[i], "nocompress") == 0)
1345 else if(strcmp(field[i], "fax") == 0)
1349 syslog(0, LOGFILE, "dialing %s speed=%d %s", number, speed, fax==Ok?"fax":"");
1351 err = modemtype(d, speed, fax == Ok);
1356 * extented Hayes commands, meaning depends on modem (VGA all over again)
1360 applyspecial(d, Cfclass0);
1361 applyspecial(d, Cerrorcorrection);
1363 compress = applyspecial(d, Ccompression);
1365 rateadjust = applyspecial(d, Crateadjust);
1367 applyspecial(d, Cflowctl);
1370 sprint(dialstr, "ATD%c%s\r", pulsed ? 'P' : 'T', number);
1371 if(send(d, dialstr) < 0)
1375 return 0; /* fax sender worries about the rest */
1377 switch(readmsg(d, 120, 0)){
1384 /* change line rate if not compressing */
1385 if(rateadjust == Ok)
1386 setspeed(d, getspeed(d->msgbuf, d->speed));
1392 * start a receiving process
1405 switch(rfork(RFPROC|RFMEM|RFFDG|RFNAMEG)){
1409 fd = open("/srv/telco", ORDWR);
1411 syslog(0, LOGFILE, "can't open telco: %r");
1414 if(mount(fd, -1, "/net", MAFTER, "") < 0){
1415 syslog(0, LOGFILE, "can't mount: %r");
1420 /* open connection through the file system interface */
1421 sprint(file, "/net/telco/%ld/data", d - dev);
1422 fd = open(file, ORDWR);
1424 syslog(0, LOGFILE, "can't open %s: %r", file);
1428 /* let parent continue */
1432 /* answer the phone and see what flavor call this is */
1433 prog = "/bin/service/telcodata";
1434 switch(apply(d, "ATA", "+FCON", 30)){
1438 prog = "/bin/service/telcofax";
1441 syslog(0, LOGFILE, "bad ATA response");
1445 /* fork a receiving process */
1450 argv[argc++] = strrchr(prog, '/')+1;
1451 argv[argc++] = file;
1452 argv[argc++] = dev->t->name;
1455 syslog(0, LOGFILE, "can't exec %s: %r\n", prog);
1458 /* wait till child gets the device open */
1460 read(pfd[0], file, 1);
1467 * hang up an connections in progress
1472 write(d->ctl, "d0", 2);
1473 write(d->ctl, "r0", 2);
1475 write(d->ctl, "r1", 2);
1476 write(d->ctl, "d1", 2);
1477 modemtype(d, maxspeed, 1);
1481 * read till we see a message or we time out
1484 readmsg(Dev *d, int secs, char *substr)
1493 len = sizeof(d->msgbuf) - 1;
1494 for(start = time(0); time(0) <= start+secs;){
1495 if(len && d->rp == d->wp){
1499 i = getinput(d, p, 1);
1502 if(*p == '\n' || *p == '\r' || len == 0){
1504 if(verbose && p != d->msgbuf)
1505 syslog(0, LOGFILE, "<-%s", d->msgbuf);
1506 if(substr && strstr(d->msgbuf, substr))
1508 for(pp = msgs; pp->text; pp++)
1509 if(strncmp(pp->text, d->msgbuf, strlen(pp->text))==0)
1510 return found ? Found : pp->type;
1513 len = sizeof(d->msgbuf) - 1;
1519 strcpy(d->msgbuf, "No response from modem");
1520 return found ? Found : Noise;
1524 * get baud rate from a connect message
1527 getspeed(char *msg, int speed)
1532 p = msg + sizeof("CONNECT") - 1;
1533 while(*p == ' ' || *p == '\t')
1543 * set speed and RTS/CTS modem flow control
1546 setspeed(Dev *d, int baud)
1552 sprint(buf, "b%d", baud);
1553 write(d->ctl, buf, strlen(buf));
1554 write(d->ctl, "m1", 2);