1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include <auth.h>
5 #include <fcall.h>
6 #include <libsec.h>
7 #include "usb.h"
8 #include "audio.h"
9 #include "audioctl.h"
10
11 int attachok;
12
13 #define STACKSIZE 16*1024
14
15 enum
16 {
17 OPERM = 0x3, /* mask of all permission types in open mode */
18 };
19
20 typedef struct Fid Fid;
21 typedef struct Audioctldata Audioctldata;
22 typedef struct Worker Worker;
23
24 struct Audioctldata
25 {
26 long offoff; /* offset of the offset for audioctl */
27 long values[2][Ncontrol][8]; /* last values transmitted */
28 char *s;
29 int ns;
30 };
31
32 enum {
33 Busy = 0x01,
34 Open = 0x02,
35 Eof = 0x04,
36 };
37
38 struct Fid
39 {
40 QLock;
41 int fid;
42 Dir *dir;
43 ushort flags;
44 short readers;
45 void *fiddata; /* file specific per-fid data (used for audioctl) */
46 Fid *next;
47 };
48
49 struct Worker
50 {
51 Fid *fid;
52 ushort tag;
53 Fcall *rhdr;
54 Dir *dir;
55 Channel *eventc;
56 Worker *next;
57 };
58
59 enum {
60 /* Event channel messages for worker */
61 Work = 0x01,
62 Check = 0x02,
63 Flush = 0x03,
64 };
65
66 enum {
67 Qdir,
68 Qvolume,
69 Qaudioctl,
70 Qaudiostat,
71 Nqid,
72 };
73
74 Dir dirs[] = {
75 [Qdir] = {0,0,{Qdir, 0,QTDIR},0555|DMDIR,0,0,0, ".", nil,nil,nil},
76 [Qvolume] = {0,0,{Qvolume, 0,QTFILE},0666,0,0,0, "volume", nil,nil,nil},
77 [Qaudioctl] = {0,0,{Qaudioctl, 0,QTFILE},0666,0,0,0, "audioctl",nil,nil,nil},
78 [Qaudiostat] = {0,0,{Qaudiostat,0,QTFILE},0666,0,0,0, "audiostat",nil,nil,nil},
79 };
80
81 int messagesize = 4*1024+IOHDRSZ;
82 uchar mdata[8*1024+IOHDRSZ];
83 uchar mbuf[8*1024+IOHDRSZ];
84
85 Fcall thdr;
86 Fcall rhdr;
87 Worker *workers;
88
89 char srvfile[64], mntdir[64], epdata[64], audiofile[64];
90 int mfd[2], p[2];
91 char user[32];
92 char *srvpost;
93
94 Channel *procchan;
95 Channel *replchan;
96
97 Fid *fids;
98
99 Fid* newfid(int);
100 void io(void *);
101 void usage(void);
102
103 extern char *mntpt;
104
105 char *rflush(Fid*), *rauth(Fid*),
106 *rattach(Fid*), *rwalk(Fid*),
107 *ropen(Fid*), *rcreate(Fid*),
108 *rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
109 *rremove(Fid*), *rstat(Fid*), *rwstat(Fid*),
110 *rversion(Fid*);
111
112 char *(*fcalls[])(Fid*) = {
113 [Tflush] rflush,
114 [Tversion] rversion,
115 [Tauth] rauth,
116 [Tattach] rattach,
117 [Twalk] rwalk,
118 [Topen] ropen,
119 [Tcreate] rcreate,
120 [Tread] rread,
121 [Twrite] rwrite,
122 [Tclunk] rclunk,
123 [Tremove] rremove,
124 [Tstat] rstat,
125 [Twstat] rwstat,
126 };
127
128 char Eperm[] = "permission denied";
129 char Enotdir[] = "not a directory";
130 char Enoauth[] = "no authentication in ramfs";
131 char Enotexist[] = "file does not exist";
132 char Einuse[] = "file in use";
133 char Eexist[] = "file exists";
134 char Enotowner[] = "not owner";
135 char Eisopen[] = "file already open for I/O";
136 char Excl[] = "exclusive use file already open";
137 char Ename[] = "illegal name";
138 char Ebadctl[] = "unknown control message";
139
140 int
notifyf(void *,char * s)141 notifyf(void *, char *s)
142 {
143 if(strncmp(s, "interrupt", 9) == 0)
144 return 1;
145 return 0;
146 }
147
148 void
post(char * name,char * envname,int srvfd)149 post(char *name, char *envname, int srvfd)
150 {
151 int fd;
152 char buf[32];
153
154 fd = create(name, OWRITE, attachok?0666:0600);
155 if(fd < 0)
156 return;
157 snprint(buf, sizeof buf, "%d", srvfd);
158 if(write(fd, buf, strlen(buf)) != strlen(buf))
159 sysfatal("srv write");
160 close(fd);
161 putenv(envname, name);
162 }
163
164 /*
165 * BUG: If audio is later used on a different name space, the
166 * audio/audioin files are not there because of the bind trick.
167 * We should actually implement those files despite the binds.
168 * If audio is used from within the same ns nothing would change,
169 * otherwise, whoever would mount the audio files could still
170 * play/record audio (unlike now).
171 */
172 void
serve(void *)173 serve(void *)
174 {
175 int i;
176 ulong t;
177
178 if(pipe(p) < 0)
179 sysfatal("pipe failed");
180 mfd[0] = p[0];
181 mfd[1] = p[0];
182
183 atnotify(notifyf, 1);
184 strcpy(user, getuser());
185 t = time(nil);
186 for(i = 0; i < Nqid; i++){
187 dirs[i].uid = user;
188 dirs[i].gid = user;
189 dirs[i].muid = user;
190 dirs[i].atime = t;
191 dirs[i].mtime = t;
192 }
193 if(mntpt == nil){
194 snprint(mntdir, sizeof(mntdir), "/dev");
195 mntpt = mntdir;
196 }
197
198 if(usbdebug)
199 fmtinstall('F', fcallfmt);
200
201 procrfork(io, nil, STACKSIZE, RFFDG|RFNAMEG);
202
203 close(p[0]); /* don't deadlock if child fails */
204 if(srvpost){
205 snprint(srvfile, sizeof srvfile, "/srv/%s", srvpost);
206 remove(srvfile);
207 post(srvfile, "usbaudio", p[1]);
208 }
209 if(mount(p[1], -1, mntpt, MBEFORE, "") < 0)
210 sysfatal("mount failed");
211 if(endpt[Play] >= 0 && devctl(epdev[Play], "name audio") < 0)
212 fprint(2, "audio: name audio: %r\n");
213 if(endpt[Record] >= 0 && devctl(epdev[Record], "name audioin") < 0)
214 fprint(2, "audio: name audioin: %r\n");
215 threadexits(nil);
216 }
217
218 char*
rversion(Fid *)219 rversion(Fid*)
220 {
221 Fid *f;
222
223 if(thdr.msize < 256)
224 return "max messagesize too small";
225 if(thdr.msize < messagesize)
226 messagesize = thdr.msize;
227 rhdr.msize = messagesize;
228 if(strncmp(thdr.version, "9P2000", 6) != 0)
229 return "unknown 9P version";
230 else
231 rhdr.version = "9P2000";
232 for(f = fids; f; f = f->next)
233 if(f->flags & Busy)
234 rclunk(f);
235 return nil;
236 }
237
238 char*
rauth(Fid *)239 rauth(Fid*)
240 {
241 return "usbaudio: no authentication required";
242 }
243
244 char*
rflush(Fid *)245 rflush(Fid *)
246 {
247 Worker *w;
248 int waitflush;
249
250 do {
251 waitflush = 0;
252 for(w = workers; w; w = w->next)
253 if(w->tag == thdr.oldtag){
254 waitflush++;
255 nbsendul(w->eventc, thdr.oldtag << 16 | Flush);
256 }
257 if(waitflush)
258 sleep(50);
259 } while(waitflush);
260 dprint(2, "flush done on tag %d\n", thdr.oldtag);
261 return 0;
262 }
263
264 char*
rattach(Fid * f)265 rattach(Fid *f)
266 {
267 f->flags |= Busy;
268 f->dir = &dirs[Qdir];
269 rhdr.qid = f->dir->qid;
270 if(attachok == 0 && strcmp(thdr.uname, user) != 0)
271 return Eperm;
272 return 0;
273 }
274
275 static Fid*
doclone(Fid * f,int nfid)276 doclone(Fid *f, int nfid)
277 {
278 Fid *nf;
279
280 nf = newfid(nfid);
281 if(nf->flags & Busy)
282 return nil;
283 nf->flags |= Busy;
284 nf->flags &= ~Open;
285 nf->dir = f->dir;
286 return nf;
287 }
288
289 char*
dowalk(Fid * f,char * name)290 dowalk(Fid *f, char *name)
291 {
292 int t;
293
294 if(strcmp(name, ".") == 0)
295 return nil;
296 if(strcmp(name, "..") == 0){
297 f->dir = &dirs[Qdir];
298 return nil;
299 }
300 if(f->dir != &dirs[Qdir])
301 return Enotexist;
302 for(t = 1; t < Nqid; t++){
303 if(strcmp(name, dirs[t].name) == 0){
304 f->dir = &dirs[t];
305 return nil;
306 }
307 }
308 return Enotexist;
309 }
310
311 char*
rwalk(Fid * f)312 rwalk(Fid *f)
313 {
314 Fid *nf;
315 char *rv;
316 int i;
317 Dir *savedir;
318
319 if(f->flags & Open)
320 return Eisopen;
321
322 rhdr.nwqid = 0;
323 nf = nil;
324 savedir = f->dir;
325 /* clone if requested */
326 if(thdr.newfid != thdr.fid){
327 nf = doclone(f, thdr.newfid);
328 if(nf == nil)
329 return "new fid in use";
330 f = nf;
331 }
332
333 /* if it's just a clone, return */
334 if(thdr.nwname == 0 && nf != nil)
335 return nil;
336
337 /* walk each element */
338 rv = nil;
339 for(i = 0; i < thdr.nwname; i++){
340 rv = dowalk(f, thdr.wname[i]);
341 if(rv != nil){
342 if(nf != nil)
343 rclunk(nf);
344 else
345 f->dir = savedir;
346 break;
347 }
348 rhdr.wqid[i] = f->dir->qid;
349 }
350 rhdr.nwqid = i;
351
352 /* we only error out if no walk */
353 if(i > 0)
354 rv = nil;
355
356 return rv;
357 }
358
359 Audioctldata *
allocaudioctldata(void)360 allocaudioctldata(void)
361 {
362 int i, j, k;
363 Audioctldata *a;
364
365 a = emallocz(sizeof(Audioctldata), 1);
366 for(i = 0; i < 2; i++)
367 for(j=0; j < Ncontrol; j++)
368 for(k=0; k < 8; k++)
369 a->values[i][j][k] = Undef;
370 return a;
371 }
372
373 char *
ropen(Fid * f)374 ropen(Fid *f)
375 {
376 if(f->flags & Open)
377 return Eisopen;
378
379 if(thdr.mode != OREAD && (f->dir->mode & 0x2) == 0)
380 return Eperm;
381 qlock(f);
382 if(f->dir == &dirs[Qaudioctl] && f->fiddata == nil)
383 f->fiddata = allocaudioctldata();
384 qunlock(f);
385 rhdr.iounit = 0;
386 rhdr.qid = f->dir->qid;
387 f->flags |= Open;
388 return nil;
389 }
390
391 char *
rcreate(Fid *)392 rcreate(Fid*)
393 {
394 return Eperm;
395 }
396
397 int
readtopdir(Fid *,uchar * buf,long off,int cnt,int blen)398 readtopdir(Fid*, uchar *buf, long off, int cnt, int blen)
399 {
400 int i, m, n;
401 long pos;
402
403 n = 0;
404 pos = 0;
405 for(i = 1; i < Nqid; i++){
406 m = convD2M(&dirs[i], &buf[n], blen-n);
407 if(off <= pos){
408 if(m <= BIT16SZ || m > cnt)
409 break;
410 n += m;
411 cnt -= m;
412 }
413 pos += m;
414 }
415 return n;
416 }
417
418 enum { Chunk = 1024, };
419
420 int
makeaudioctldata(Fid * f)421 makeaudioctldata(Fid *f)
422 {
423 int rec, ctl, i, diff;
424 long *actls; /* 8 of them */
425 char *p, *e;
426 Audiocontrol *c;
427 Audioctldata *a;
428
429 if((a = f->fiddata) == nil)
430 sysfatal("fiddata");
431 if((p = a->s) == nil)
432 a->s = p = emallocz(Chunk, 0);
433 e = p + Chunk - 1; /* e must point *at* last byte, not *after* */
434 for(rec = 0; rec < 2; rec++)
435 for(ctl = 0; ctl < Ncontrol; ctl++){
436 c = &controls[rec][ctl];
437 actls = a->values[rec][ctl];
438 diff = 0;
439 if(c->chans){
440 for(i = 1; i < 8; i++)
441 if((c->chans & 1<<i) &&
442 c->value[i] != actls[i])
443 diff = 1;
444 }else
445 if(c->value[0] != actls[0])
446 diff = 1;
447 if(diff){
448 p = seprint(p, e, "%s %s %A", c->name,
449 rec? "in": "out", c);
450 memmove(actls, c->value, sizeof c->value);
451 if(c->min != Undef){
452 p = seprint(p, e, " %ld %ld", c->min,
453 c->max);
454 if(c->step != Undef)
455 p = seprint(p, e, " %ld",
456 c->step);
457 }
458 p = seprint(p, e, "\n");
459 }
460 }
461 assert(strlen(a->s) < Chunk);
462 a->ns = p - a->s;
463 return a->ns;
464 }
465
466 void
readproc(void * x)467 readproc(void *x)
468 {
469 int n, cnt;
470 ulong event;
471 vlong off;
472 uchar *mdata;
473 Audioctldata *a;
474 Fcall *rhdr;
475 Fid *f;
476 Worker *w;
477
478 w = x;
479 mdata = emallocz(8*1024+IOHDRSZ, 0);
480 while(event = recvul(w->eventc)){
481 if(event != Work)
482 continue;
483 f = w->fid;
484 rhdr = w->rhdr;
485 a = f->fiddata;
486 off = rhdr->offset;
487 cnt = rhdr->count;
488 assert(a->offoff == off);
489 /* f is already locked */
490 for(;;){
491 qunlock(f);
492 event = recvul(w->eventc);
493 qlock(f);
494 ddprint(2, "readproc unblocked fid %d %lld\n",
495 f->fid, f->dir->qid.path);
496 switch (event & 0xffff){
497 case Work:
498 sysfatal("readproc phase error");
499 case Check:
500 if(f->fiddata && makeaudioctldata(f) == 0)
501 continue;
502 break;
503 case Flush:
504 if((event >> 16) == rhdr->tag){
505 ddprint(2, "readproc flushing fid %d, tag %d\n",
506 f->fid, rhdr->tag);
507 goto flush;
508 }
509 continue;
510 }
511 if(f->fiddata){
512 rhdr->data = a->s;
513 rhdr->count = a->ns;
514 break;
515 }
516 yield();
517 }
518 if(rhdr->count > cnt)
519 rhdr->count = cnt;
520 if(rhdr->count)
521 f->flags &= ~Eof;
522 ddprint(2, "readproc:->%F\n", rhdr);
523 n = convS2M(rhdr, mdata, messagesize);
524 if(write(mfd[1], mdata, n) != n)
525 sysfatal("mount write");
526 flush:
527 w->tag = NOTAG;
528 f->readers--;
529 assert(f->readers == 0);
530 free(rhdr);
531 w->rhdr = nil;
532 qunlock(f);
533 sendp(procchan, w);
534 }
535 threadexits(nil);
536 }
537
538 char*
rread(Fid * f)539 rread(Fid *f)
540 {
541 int i, n, cnt, rec, div;
542 vlong off;
543 char *p;
544 Audiocontrol *c;
545 Audioctldata *a;
546 Worker *w;
547 static char buf[1024];
548
549 rhdr.count = 0;
550 off = thdr.offset;
551 cnt = thdr.count;
552
553 if(cnt > messagesize - IOHDRSZ)
554 cnt = messagesize - IOHDRSZ;
555
556 rhdr.data = (char*)mbuf;
557
558 if(f->dir == &dirs[Qdir]){
559 n = readtopdir(f, mbuf, off, cnt, messagesize - IOHDRSZ);
560 rhdr.count = n;
561 return nil;
562 }
563
564 if(f->dir == &dirs[Qvolume]){
565 p = buf;
566 n = sizeof buf;
567 for(rec = 0; rec < 2; rec++){
568 c = &controls[rec][Volume_control];
569 if(c->readable){
570 div = c->max - c->min;
571 i = snprint(p, n, "audio %s %ld\n",
572 rec? "in": "out", (c->min != Undef?
573 100*(c->value[0]-c->min)/(div? div: 1):
574 c->value[0]));
575 p += i;
576 n -= i;
577 }
578 c = &controls[rec][Treble_control];
579 if(c->readable){
580 div = c->max - c->min;
581 i = snprint(p, n, "treb %s %ld\n",
582 rec? "in": "out", (c->min != Undef?
583 100*(c->value[0]-c->min)/(div? div: 1):
584 c->value[0]));
585 p += i;
586 n -= i;
587 }
588 c = &controls[rec][Bass_control];
589 if(c->readable){
590 div = c->max - c->min;
591 i = snprint(p, n, "bass %s %ld\n",
592 rec? "in": "out", (c->min != Undef?
593 100*(c->value[0]-c->min)/(div? div: 1):
594 c->value[0]));
595 p += i;
596 n -= i;
597 }
598 c = &controls[rec][Speed_control];
599 if(c->readable){
600 i = snprint(p, n, "speed %s %ld\n",
601 rec? "in": "out", c->value[0]);
602 p += i;
603 n -= i;
604 }
605 }
606 n = sizeof buf - n;
607 if(off > n)
608 rhdr.count = 0;
609 else{
610 rhdr.data = buf + off;
611 rhdr.count = n - off;
612 if(rhdr.count > cnt)
613 rhdr.count = cnt;
614 }
615 return nil;
616 }
617
618 if(f->dir == &dirs[Qaudioctl]){
619 Fcall *hdr;
620
621 qlock(f);
622 a = f->fiddata;
623 if(off - a->offoff < 0){
624 /* there was a seek */
625 a->offoff = off;
626 a->ns = 0;
627 }
628 do {
629 if(off - a->offoff < a->ns){
630 rhdr.data = a->s + (off - a->offoff);
631 rhdr.count = a->ns - (off - a->offoff);
632 if(rhdr.count > cnt)
633 rhdr.count = cnt;
634 qunlock(f);
635 return nil;
636 }
637 if(a->offoff != off){
638 a->ns = 0;
639 a->offoff = off;
640 rhdr.count = 0;
641 qunlock(f);
642 return nil;
643 }
644 } while(makeaudioctldata(f) != 0);
645
646 assert(a->offoff == off);
647 /* Wait for data off line */
648 f->readers++;
649 w = nbrecvp(procchan);
650 if(w == nil){
651 w = emallocz(sizeof(Worker), 1);
652 w->eventc = chancreate(sizeof(ulong), 1);
653 w->next = workers;
654 workers = w;
655 proccreate(readproc, w, 4096);
656 }
657 hdr = emallocz(sizeof(Fcall), 0);
658 w->fid = f;
659 w->tag = thdr.tag;
660 assert(w->rhdr == nil);
661 w->rhdr = hdr;
662 hdr->count = cnt;
663 hdr->offset = off;
664 hdr->type = thdr.type+1;
665 hdr->fid = thdr.fid;
666 hdr->tag = thdr.tag;
667 sendul(w->eventc, Work);
668 return (char*)~0;
669 }
670
671 return Eperm;
672 }
673
674 char*
rwrite(Fid * f)675 rwrite(Fid *f)
676 {
677 long cnt, value;
678 char *lines[2*Ncontrol], *fields[4], *subfields[9], *err, *p;
679 int nlines, i, nf, nnf, rec, ctl;
680 Audiocontrol *c;
681 Worker *w;
682 static char buf[256];
683
684 rhdr.count = 0;
685 cnt = thdr.count;
686
687 if(cnt > messagesize - IOHDRSZ)
688 cnt = messagesize - IOHDRSZ;
689
690 err = nil;
691 if(f->dir == &dirs[Qvolume] || f->dir == &dirs[Qaudioctl]){
692 thdr.data[cnt] = '\0';
693 nlines = getfields(thdr.data, lines, 2*Ncontrol, 1, "\n");
694 for(i = 0; i < nlines; i++){
695 dprint(2, "line: %s\n", lines[i]);
696 nf = tokenize(lines[i], fields, 4);
697 if(nf == 0)
698 continue;
699 if(nf == 3)
700 if(strcmp(fields[1], "in") == 0 ||
701 strcmp(fields[1], "record") == 0)
702 rec = 1;
703 else if(strcmp(fields[1], "out") == 0 ||
704 strcmp(fields[1], "playback") == 0)
705 rec = 0;
706 else{
707 dprint(2, "bad1\n");
708 return Ebadctl;
709 }
710 else if(nf == 2)
711 rec = 0;
712 else{
713 dprint(2, "bad2 %d\n", nf);
714 return Ebadctl;
715 }
716 c = nil;
717 if(strcmp(fields[0], "audio") == 0) /* special case */
718 fields[0] = "volume";
719 for(ctl = 0; ctl < Ncontrol; ctl++){
720 c = &controls[rec][ctl];
721 if(strcmp(fields[0], c->name) == 0)
722 break;
723 }
724 if(ctl == Ncontrol){
725 dprint(2, "bad3\n");
726 return Ebadctl;
727 }
728 if(f->dir == &dirs[Qvolume] && ctl != Speed_control &&
729 c->min != Undef && c->max != Undef){
730 nnf = tokenize(fields[nf-1], subfields,
731 nelem(subfields));
732 if(nnf <= 0 || nnf > 8){
733 dprint(2, "bad4\n");
734 return Ebadctl;
735 }
736 p = buf;
737 for(i = 0; i < nnf; i++){
738 value = strtol(subfields[i], nil, 0);
739 value = ((100 - value)*c->min +
740 value*c->max) / 100;
741 if(p == buf){
742 dprint(2, "rwrite: %s %s '%ld",
743 c->name, rec?
744 "record":
745 "playback",
746 value);
747 }else
748 dprint(2, " %ld", value);
749 if(p == buf)
750 p = seprint(p, buf+sizeof buf,
751 "0x%p %s %s '%ld",
752 replchan, c->name, rec?
753 "record": "playback",
754 value);
755 else
756 p = seprint(p, buf+sizeof buf,
757 " %ld", value);
758 }
759 dprint(2, "'\n");
760 seprint(p, buf+sizeof buf-1, "'");
761 chanprint(controlchan, buf);
762 }else{
763 dprint(2, "rwrite: %s %s %q", c->name,
764 rec? "record": "playback",
765 fields[nf-1]);
766 chanprint(controlchan, "0x%p %s %s %q",
767 replchan, c->name, rec? "record":
768 "playback", fields[nf-1]);
769 }
770 p = recvp(replchan);
771 if(p){
772 if(strcmp(p, "ok") == 0){
773 free(p);
774 p = nil;
775 }
776 if(err == nil)
777 err = p;
778 }
779 }
780 for(w = workers; w; w = w->next)
781 nbsendul(w->eventc, Qaudioctl << 16 | Check);
782 rhdr.count = thdr.count;
783 return err;
784 }
785 return Eperm;
786 }
787
788 char *
rclunk(Fid * f)789 rclunk(Fid *f)
790 {
791 Audioctldata *a;
792
793 qlock(f);
794 f->flags &= ~(Open|Busy);
795 assert(f->readers ==0);
796 if(f->fiddata){
797 a = f->fiddata;
798 if(a->s)
799 free(a->s);
800 free(a);
801 f->fiddata = nil;
802 }
803 qunlock(f);
804 return 0;
805 }
806
807 char *
rremove(Fid *)808 rremove(Fid *)
809 {
810 return Eperm;
811 }
812
813 char *
rstat(Fid * f)814 rstat(Fid *f)
815 {
816 Audioctldata *a;
817
818 if(f->dir == &dirs[Qaudioctl]){
819 qlock(f);
820 if(f->fiddata == nil)
821 f->fiddata = allocaudioctldata();
822 a = f->fiddata;
823 if(a->ns == 0)
824 makeaudioctldata(f);
825 f->dir->length = a->offoff + a->ns;
826 qunlock(f);
827 }
828 rhdr.nstat = convD2M(f->dir, mbuf, messagesize - IOHDRSZ);
829 rhdr.stat = mbuf;
830 return 0;
831 }
832
833 char *
rwstat(Fid *)834 rwstat(Fid*)
835 {
836 return Eperm;
837 }
838
839 Fid *
newfid(int fid)840 newfid(int fid)
841 {
842 Fid *f, *ff;
843
844 ff = nil;
845 for(f = fids; f; f = f->next)
846 if(f->fid == fid)
847 return f;
848 else if(ff == nil && (f->flags & Busy) == 0)
849 ff = f;
850 if(ff == nil){
851 ff = emallocz(sizeof *ff, 1);
852 ff->next = fids;
853 fids = ff;
854 }
855 ff->fid = fid;
856 ff->flags &= ~(Busy|Open);
857 ff->dir = nil;
858 return ff;
859 }
860
861 void
io(void *)862 io(void *)
863 {
864 char *err, e[32];
865 int n;
866
867 close(p[1]);
868
869 procchan = chancreate(sizeof(Channel*), 8);
870 replchan = chancreate(sizeof(char*), 0);
871 for(;;){
872 /*
873 * reading from a pipe or a network device
874 * will give an error after a few eof reads
875 * however, we cannot tell the difference
876 * between a zero-length read and an interrupt
877 * on the processes writing to us,
878 * so we wait for the error
879 */
880 n = read9pmsg(mfd[0], mdata, messagesize);
881 if(n == 0)
882 continue;
883 if(n < 0){
884 rerrstr(e, sizeof e);
885 if(strcmp(e, "interrupted") == 0){
886 dprint(2, "read9pmsg interrupted\n");
887 continue;
888 }
889 return;
890 }
891 if(convM2S(mdata, n, &thdr) == 0)
892 continue;
893
894 ddprint(2, "io:<-%F\n", &thdr);
895
896 rhdr.data = (char*)mdata + messagesize;
897 if(!fcalls[thdr.type])
898 err = "bad fcall type";
899 else
900 err = (*fcalls[thdr.type])(newfid(thdr.fid));
901 if(err == (char*)~0)
902 continue; /* handled off line */
903 if(err){
904 rhdr.type = Rerror;
905 rhdr.ename = err;
906 }else{
907 rhdr.type = thdr.type + 1;
908 rhdr.fid = thdr.fid;
909 }
910 rhdr.tag = thdr.tag;
911 ddprint(2, "io:->%F\n", &rhdr);
912 n = convS2M(&rhdr, mdata, messagesize);
913 if(write(mfd[1], mdata, n) != n)
914 sysfatal("mount write");
915 }
916 }
917
918 int
newid(void)919 newid(void)
920 {
921 int rv;
922 static int id;
923 static Lock idlock;
924
925 lock(&idlock);
926 rv = ++id;
927 unlock(&idlock);
928
929 return rv;
930 }
931
932 void
ctlevent(void)933 ctlevent(void)
934 {
935 Worker *w;
936
937 for(w = workers; w; w = w->next)
938 nbsendul(w->eventc, Qaudioctl << 16 | Check);
939 }
940