Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
141
notifyf(void *, char *s)
142
{
143
	if(strncmp(s, "interrupt", 9) == 0)
144
		return 1;
145
	return 0;
146
}
147
 
148
void
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
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*
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*
239
rauth(Fid*)
240
{
241
	return "usbaudio: no authentication required";
242
}
243
 
244
char*
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*
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*
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*
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*
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 *
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 *
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 *
392
rcreate(Fid*)
393
{
394
	return Eperm;
395
}
396
 
397
int
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
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
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*
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*
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 *
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 *
808
rremove(Fid *)
809
{
810
	return Eperm;
811
}
812
 
813
char *
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 *
834
rwstat(Fid*)
835
{
836
	return Eperm;
837
}
838
 
839
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
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
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
933
ctlevent(void)
934
{
935
	Worker *w;
936
 
937
	for(w = workers; w; w = w->next)
938
		nbsendul(w->eventc, Qaudioctl << 16 | Check);
939
}