xref: /plan9/sys/src/cmd/usb/audio/audiofs.c (revision e9b54818716d7e3e58c57fc2d88eb8b05defef08)
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