xref: /plan9-contrib/sys/src/cmd/usb/audio/audiofs.c (revision e9b54818716d7e3e58c57fc2d88eb8b05defef08)
17f0337cdSDavid du Colombier #include <u.h>
27f0337cdSDavid du Colombier #include <libc.h>
37f0337cdSDavid du Colombier #include <thread.h>
47f0337cdSDavid du Colombier #include <auth.h>
57f0337cdSDavid du Colombier #include <fcall.h>
67f0337cdSDavid du Colombier #include <libsec.h>
77f0337cdSDavid du Colombier #include "usb.h"
8906943f9SDavid du Colombier #include "audio.h"
9906943f9SDavid du Colombier #include "audioctl.h"
107f0337cdSDavid du Colombier 
115ab4dd4cSDavid du Colombier int attachok;
125ab4dd4cSDavid du Colombier 
137f0337cdSDavid du Colombier #define STACKSIZE 16*1024
147f0337cdSDavid du Colombier 
157f0337cdSDavid du Colombier enum
167f0337cdSDavid du Colombier {
172ddf2468SDavid du Colombier 	OPERM	= 0x3,	/* mask of all permission types in open mode */
187f0337cdSDavid du Colombier };
197f0337cdSDavid du Colombier 
207f0337cdSDavid du Colombier typedef struct Fid Fid;
217f0337cdSDavid du Colombier typedef struct Audioctldata Audioctldata;
227f0337cdSDavid du Colombier typedef struct Worker Worker;
237f0337cdSDavid du Colombier 
247f0337cdSDavid du Colombier struct Audioctldata
257f0337cdSDavid du Colombier {
262ddf2468SDavid du Colombier 	long	offoff;			/* offset of the offset for audioctl */
272ddf2468SDavid du Colombier 	long	values[2][Ncontrol][8];	/* last values transmitted */
287f0337cdSDavid du Colombier 	char	*s;
297f0337cdSDavid du Colombier 	int	ns;
307f0337cdSDavid du Colombier };
317f0337cdSDavid du Colombier 
327f0337cdSDavid du Colombier enum {
337f0337cdSDavid du Colombier 	Busy =	0x01,
347f0337cdSDavid du Colombier 	Open =	0x02,
357f0337cdSDavid du Colombier 	Eof =	0x04,
367f0337cdSDavid du Colombier };
377f0337cdSDavid du Colombier 
387f0337cdSDavid du Colombier struct Fid
397f0337cdSDavid du Colombier {
407f0337cdSDavid du Colombier 	QLock;
417f0337cdSDavid du Colombier 	int	fid;
427f0337cdSDavid du Colombier 	Dir	*dir;
437f0337cdSDavid du Colombier 	ushort	flags;
447f0337cdSDavid du Colombier 	short	readers;
452ddf2468SDavid du Colombier 	void	*fiddata;  /* file specific per-fid data (used for audioctl) */
467f0337cdSDavid du Colombier 	Fid	*next;
477f0337cdSDavid du Colombier };
487f0337cdSDavid du Colombier 
497f0337cdSDavid du Colombier struct Worker
507f0337cdSDavid du Colombier {
517f0337cdSDavid du Colombier 	Fid	*fid;
527f0337cdSDavid du Colombier 	ushort	tag;
537f0337cdSDavid du Colombier 	Fcall	*rhdr;
547f0337cdSDavid du Colombier 	Dir	*dir;
557f0337cdSDavid du Colombier 	Channel	*eventc;
567f0337cdSDavid du Colombier 	Worker	*next;
577f0337cdSDavid du Colombier };
587f0337cdSDavid du Colombier 
597f0337cdSDavid du Colombier enum {
602ddf2468SDavid du Colombier 	/* Event channel messages for worker */
617f0337cdSDavid du Colombier 	Work =	0x01,
627f0337cdSDavid du Colombier 	Check =	0x02,
637f0337cdSDavid du Colombier 	Flush =	0x03,
647f0337cdSDavid du Colombier };
657f0337cdSDavid du Colombier 
667f0337cdSDavid du Colombier enum {
677f0337cdSDavid du Colombier 	Qdir,
687f0337cdSDavid du Colombier 	Qvolume,
697f0337cdSDavid du Colombier 	Qaudioctl,
707f0337cdSDavid du Colombier 	Qaudiostat,
717f0337cdSDavid du Colombier 	Nqid,
727f0337cdSDavid du Colombier };
737f0337cdSDavid du Colombier 
747f0337cdSDavid du Colombier Dir dirs[] = {
757f0337cdSDavid du Colombier [Qdir] =		{0,0,{Qdir,      0,QTDIR},0555|DMDIR,0,0,0, ".",  nil,nil,nil},
767f0337cdSDavid du Colombier [Qvolume] =	{0,0,{Qvolume,   0,QTFILE},0666,0,0,0,	"volume", nil,nil,nil},
777f0337cdSDavid du Colombier [Qaudioctl] =	{0,0,{Qaudioctl, 0,QTFILE},0666,0,0,0,	"audioctl",nil,nil,nil},
787f0337cdSDavid du Colombier [Qaudiostat] =	{0,0,{Qaudiostat,0,QTFILE},0666,0,0,0,	"audiostat",nil,nil,nil},
797f0337cdSDavid du Colombier };
807f0337cdSDavid du Colombier 
817f0337cdSDavid du Colombier int	messagesize = 4*1024+IOHDRSZ;
827f0337cdSDavid du Colombier uchar	mdata[8*1024+IOHDRSZ];
837f0337cdSDavid du Colombier uchar	mbuf[8*1024+IOHDRSZ];
847f0337cdSDavid du Colombier 
857f0337cdSDavid du Colombier Fcall	thdr;
867f0337cdSDavid du Colombier Fcall	rhdr;
877f0337cdSDavid du Colombier Worker *workers;
887f0337cdSDavid du Colombier 
897f0337cdSDavid du Colombier char srvfile[64], mntdir[64], epdata[64], audiofile[64];
907f0337cdSDavid du Colombier int mfd[2], p[2];
917f0337cdSDavid du Colombier char user[32];
927f0337cdSDavid du Colombier char *srvpost;
937f0337cdSDavid du Colombier 
947f0337cdSDavid du Colombier Channel *procchan;
957f0337cdSDavid du Colombier Channel *replchan;
967f0337cdSDavid du Colombier 
977f0337cdSDavid du Colombier Fid *fids;
987f0337cdSDavid du Colombier 
997f0337cdSDavid du Colombier Fid*		newfid(int);
1007f0337cdSDavid du Colombier void		io(void *);
1017f0337cdSDavid du Colombier void		usage(void);
1027f0337cdSDavid du Colombier 
1037f0337cdSDavid du Colombier extern char *mntpt;
1047f0337cdSDavid du Colombier 
1057f0337cdSDavid du Colombier char	*rflush(Fid*), *rauth(Fid*),
1067f0337cdSDavid du Colombier 	*rattach(Fid*), *rwalk(Fid*),
1077f0337cdSDavid du Colombier 	*ropen(Fid*), *rcreate(Fid*),
1087f0337cdSDavid du Colombier 	*rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
1097f0337cdSDavid du Colombier 	*rremove(Fid*), *rstat(Fid*), *rwstat(Fid*),
1107f0337cdSDavid du Colombier 	*rversion(Fid*);
1117f0337cdSDavid du Colombier 
1127f0337cdSDavid du Colombier char 	*(*fcalls[])(Fid*) = {
1137f0337cdSDavid du Colombier 	[Tflush]	rflush,
1147f0337cdSDavid du Colombier 	[Tversion]	rversion,
1157f0337cdSDavid du Colombier 	[Tauth]		rauth,
1167f0337cdSDavid du Colombier 	[Tattach]	rattach,
1177f0337cdSDavid du Colombier 	[Twalk]		rwalk,
1187f0337cdSDavid du Colombier 	[Topen]		ropen,
1197f0337cdSDavid du Colombier 	[Tcreate]	rcreate,
1207f0337cdSDavid du Colombier 	[Tread]		rread,
1217f0337cdSDavid du Colombier 	[Twrite]	rwrite,
1227f0337cdSDavid du Colombier 	[Tclunk]	rclunk,
1237f0337cdSDavid du Colombier 	[Tremove]	rremove,
1247f0337cdSDavid du Colombier 	[Tstat]		rstat,
1257f0337cdSDavid du Colombier 	[Twstat]	rwstat,
1267f0337cdSDavid du Colombier };
1277f0337cdSDavid du Colombier 
1287f0337cdSDavid du Colombier char	Eperm[] =	"permission denied";
1297f0337cdSDavid du Colombier char	Enotdir[] =	"not a directory";
1307f0337cdSDavid du Colombier char	Enoauth[] =	"no authentication in ramfs";
1317f0337cdSDavid du Colombier char	Enotexist[] =	"file does not exist";
1327f0337cdSDavid du Colombier char	Einuse[] =	"file in use";
1337f0337cdSDavid du Colombier char	Eexist[] =	"file exists";
1347f0337cdSDavid du Colombier char	Enotowner[] =	"not owner";
1357f0337cdSDavid du Colombier char	Eisopen[] = 	"file already open for I/O";
1367f0337cdSDavid du Colombier char	Excl[] = 	"exclusive use file already open";
1377f0337cdSDavid du Colombier char	Ename[] = 	"illegal name";
1387f0337cdSDavid du Colombier char	Ebadctl[] =	"unknown control message";
1397f0337cdSDavid du Colombier 
1407f0337cdSDavid du Colombier int
notifyf(void *,char * s)1417f0337cdSDavid du Colombier notifyf(void *, char *s)
1427f0337cdSDavid du Colombier {
1437f0337cdSDavid du Colombier 	if(strncmp(s, "interrupt", 9) == 0)
1447f0337cdSDavid du Colombier 		return 1;
1457f0337cdSDavid du Colombier 	return 0;
1467f0337cdSDavid du Colombier }
1477f0337cdSDavid du Colombier 
1487f0337cdSDavid du Colombier void
post(char * name,char * envname,int srvfd)1497f0337cdSDavid du Colombier post(char *name, char *envname, int srvfd)
1507f0337cdSDavid du Colombier {
1517f0337cdSDavid du Colombier 	int fd;
1527f0337cdSDavid du Colombier 	char buf[32];
1537f0337cdSDavid du Colombier 
1545ab4dd4cSDavid du Colombier 	fd = create(name, OWRITE, attachok?0666:0600);
1557f0337cdSDavid du Colombier 	if(fd < 0)
1567f0337cdSDavid du Colombier 		return;
157*e9b54818SDavid du Colombier 	snprint(buf, sizeof buf, "%d", srvfd);
1587f0337cdSDavid du Colombier 	if(write(fd, buf, strlen(buf)) != strlen(buf))
1597f0337cdSDavid du Colombier 		sysfatal("srv write");
1607f0337cdSDavid du Colombier 	close(fd);
1617f0337cdSDavid du Colombier 	putenv(envname, name);
1627f0337cdSDavid du Colombier }
1637f0337cdSDavid du Colombier 
164906943f9SDavid du Colombier /*
165906943f9SDavid du Colombier  * BUG: If audio is later used on a different name space, the
166906943f9SDavid du Colombier  * audio/audioin files are not there because of the bind trick.
167906943f9SDavid du Colombier  * We should actually implement those files despite the binds.
168906943f9SDavid du Colombier  * If audio is used from within the same ns nothing would change,
169906943f9SDavid du Colombier  * otherwise, whoever would mount the audio files could still
170906943f9SDavid du Colombier  * play/record audio (unlike now).
171906943f9SDavid du Colombier  */
1727f0337cdSDavid du Colombier void
serve(void *)1737f0337cdSDavid du Colombier serve(void *)
1747f0337cdSDavid du Colombier {
1757f0337cdSDavid du Colombier 	int i;
1767f0337cdSDavid du Colombier 	ulong t;
1777f0337cdSDavid du Colombier 
1787f0337cdSDavid du Colombier 	if(pipe(p) < 0)
1797f0337cdSDavid du Colombier 		sysfatal("pipe failed");
1807f0337cdSDavid du Colombier 	mfd[0] = p[0];
1817f0337cdSDavid du Colombier 	mfd[1] = p[0];
1827f0337cdSDavid du Colombier 
1837f0337cdSDavid du Colombier 	atnotify(notifyf, 1);
1847f0337cdSDavid du Colombier 	strcpy(user, getuser());
1857f0337cdSDavid du Colombier 	t = time(nil);
1867f0337cdSDavid du Colombier 	for(i = 0; i < Nqid; i++){
1877f0337cdSDavid du Colombier 		dirs[i].uid = user;
1887f0337cdSDavid du Colombier 		dirs[i].gid = user;
1897f0337cdSDavid du Colombier 		dirs[i].muid = user;
1907f0337cdSDavid du Colombier 		dirs[i].atime = t;
1917f0337cdSDavid du Colombier 		dirs[i].mtime = t;
1927f0337cdSDavid du Colombier 	}
1937f0337cdSDavid du Colombier 	if(mntpt == nil){
1947f0337cdSDavid du Colombier 		snprint(mntdir, sizeof(mntdir), "/dev");
1957f0337cdSDavid du Colombier 		mntpt = mntdir;
1967f0337cdSDavid du Colombier 	}
1977f0337cdSDavid du Colombier 
198906943f9SDavid du Colombier 	if(usbdebug)
1997f0337cdSDavid du Colombier 		fmtinstall('F', fcallfmt);
2007f0337cdSDavid du Colombier 
2017f0337cdSDavid du Colombier 	procrfork(io, nil, STACKSIZE, RFFDG|RFNAMEG);
2027f0337cdSDavid du Colombier 
2037f0337cdSDavid du Colombier 	close(p[0]);	/* don't deadlock if child fails */
2047f0337cdSDavid du Colombier 	if(srvpost){
205*e9b54818SDavid du Colombier 		snprint(srvfile, sizeof srvfile, "/srv/%s", srvpost);
2067f0337cdSDavid du Colombier 		remove(srvfile);
2077f0337cdSDavid du Colombier 		post(srvfile, "usbaudio", p[1]);
2087f0337cdSDavid du Colombier 	}
2097f0337cdSDavid du Colombier 	if(mount(p[1], -1, mntpt, MBEFORE, "") < 0)
2107f0337cdSDavid du Colombier 		sysfatal("mount failed");
211906943f9SDavid du Colombier 	if(endpt[Play] >= 0 && devctl(epdev[Play], "name audio") < 0)
212906943f9SDavid du Colombier 		fprint(2, "audio: name audio: %r\n");
213906943f9SDavid du Colombier 	if(endpt[Record] >= 0 && devctl(epdev[Record], "name audioin") < 0)
214906943f9SDavid du Colombier 		fprint(2, "audio: name audioin: %r\n");
2157f0337cdSDavid du Colombier 	threadexits(nil);
2167f0337cdSDavid du Colombier }
2177f0337cdSDavid du Colombier 
2187f0337cdSDavid du Colombier char*
rversion(Fid *)2197f0337cdSDavid du Colombier rversion(Fid*)
2207f0337cdSDavid du Colombier {
2217f0337cdSDavid du Colombier 	Fid *f;
2227f0337cdSDavid du Colombier 
2237f0337cdSDavid du Colombier 	if(thdr.msize < 256)
2247f0337cdSDavid du Colombier 		return "max messagesize too small";
2257f0337cdSDavid du Colombier 	if(thdr.msize < messagesize)
2267f0337cdSDavid du Colombier 		messagesize = thdr.msize;
2277f0337cdSDavid du Colombier 	rhdr.msize = messagesize;
2287f0337cdSDavid du Colombier 	if(strncmp(thdr.version, "9P2000", 6) != 0)
2297f0337cdSDavid du Colombier 		return "unknown 9P version";
2307f0337cdSDavid du Colombier 	else
2317f0337cdSDavid du Colombier 		rhdr.version = "9P2000";
2327f0337cdSDavid du Colombier 	for(f = fids; f; f = f->next)
2337f0337cdSDavid du Colombier 		if(f->flags & Busy)
2347f0337cdSDavid du Colombier 			rclunk(f);
2357f0337cdSDavid du Colombier 	return nil;
2367f0337cdSDavid du Colombier }
2377f0337cdSDavid du Colombier 
2387f0337cdSDavid du Colombier char*
rauth(Fid *)2397f0337cdSDavid du Colombier rauth(Fid*)
2407f0337cdSDavid du Colombier {
2417f0337cdSDavid du Colombier 	return "usbaudio: no authentication required";
2427f0337cdSDavid du Colombier }
2437f0337cdSDavid du Colombier 
2447f0337cdSDavid du Colombier char*
rflush(Fid *)2457f0337cdSDavid du Colombier rflush(Fid *)
2467f0337cdSDavid du Colombier {
2477f0337cdSDavid du Colombier 	Worker *w;
2487f0337cdSDavid du Colombier 	int waitflush;
2497f0337cdSDavid du Colombier 
2507f0337cdSDavid du Colombier 	do {
2517f0337cdSDavid du Colombier 		waitflush = 0;
2527f0337cdSDavid du Colombier 		for(w = workers; w; w = w->next)
2537f0337cdSDavid du Colombier 			if(w->tag == thdr.oldtag){
2547f0337cdSDavid du Colombier 				waitflush++;
2557f0337cdSDavid du Colombier 				nbsendul(w->eventc, thdr.oldtag << 16 | Flush);
2567f0337cdSDavid du Colombier 			}
2577f0337cdSDavid du Colombier 		if(waitflush)
2587f0337cdSDavid du Colombier 			sleep(50);
2597f0337cdSDavid du Colombier 	} while(waitflush);
260906943f9SDavid du Colombier 	dprint(2, "flush done on tag %d\n", thdr.oldtag);
2617f0337cdSDavid du Colombier 	return 0;
2627f0337cdSDavid du Colombier }
2637f0337cdSDavid du Colombier 
2647f0337cdSDavid du Colombier char*
rattach(Fid * f)2657f0337cdSDavid du Colombier rattach(Fid *f)
2667f0337cdSDavid du Colombier {
2677f0337cdSDavid du Colombier 	f->flags |= Busy;
2687f0337cdSDavid du Colombier 	f->dir = &dirs[Qdir];
2697f0337cdSDavid du Colombier 	rhdr.qid = f->dir->qid;
2705ab4dd4cSDavid du Colombier 	if(attachok == 0 && strcmp(thdr.uname, user) != 0)
2717f0337cdSDavid du Colombier 		return Eperm;
2727f0337cdSDavid du Colombier 	return 0;
2737f0337cdSDavid du Colombier }
2747f0337cdSDavid du Colombier 
2757f0337cdSDavid du Colombier static Fid*
doclone(Fid * f,int nfid)2767f0337cdSDavid du Colombier doclone(Fid *f, int nfid)
2777f0337cdSDavid du Colombier {
2787f0337cdSDavid du Colombier 	Fid *nf;
2797f0337cdSDavid du Colombier 
2807f0337cdSDavid du Colombier 	nf = newfid(nfid);
2817f0337cdSDavid du Colombier 	if(nf->flags & Busy)
2827f0337cdSDavid du Colombier 		return nil;
2837f0337cdSDavid du Colombier 	nf->flags |= Busy;
2847f0337cdSDavid du Colombier 	nf->flags &= ~Open;
2857f0337cdSDavid du Colombier 	nf->dir = f->dir;
2867f0337cdSDavid du Colombier 	return nf;
2877f0337cdSDavid du Colombier }
2887f0337cdSDavid du Colombier 
2897f0337cdSDavid du Colombier char*
dowalk(Fid * f,char * name)2907f0337cdSDavid du Colombier dowalk(Fid *f, char *name)
2917f0337cdSDavid du Colombier {
2927f0337cdSDavid du Colombier 	int t;
2937f0337cdSDavid du Colombier 
2947f0337cdSDavid du Colombier 	if(strcmp(name, ".") == 0)
2957f0337cdSDavid du Colombier 		return nil;
2967f0337cdSDavid du Colombier 	if(strcmp(name, "..") == 0){
2977f0337cdSDavid du Colombier 		f->dir = &dirs[Qdir];
2987f0337cdSDavid du Colombier 		return nil;
2997f0337cdSDavid du Colombier 	}
3007f0337cdSDavid du Colombier 	if(f->dir != &dirs[Qdir])
3017f0337cdSDavid du Colombier 		return Enotexist;
3027f0337cdSDavid du Colombier 	for(t = 1; t < Nqid; t++){
3037f0337cdSDavid du Colombier 		if(strcmp(name, dirs[t].name) == 0){
3047f0337cdSDavid du Colombier 			f->dir = &dirs[t];
3057f0337cdSDavid du Colombier 			return nil;
3067f0337cdSDavid du Colombier 		}
3077f0337cdSDavid du Colombier 	}
3087f0337cdSDavid du Colombier 	return Enotexist;
3097f0337cdSDavid du Colombier }
3107f0337cdSDavid du Colombier 
3117f0337cdSDavid du Colombier char*
rwalk(Fid * f)3127f0337cdSDavid du Colombier rwalk(Fid *f)
3137f0337cdSDavid du Colombier {
3147f0337cdSDavid du Colombier 	Fid *nf;
3157f0337cdSDavid du Colombier 	char *rv;
3167f0337cdSDavid du Colombier 	int i;
3177f0337cdSDavid du Colombier 	Dir *savedir;
3187f0337cdSDavid du Colombier 
3197f0337cdSDavid du Colombier 	if(f->flags & Open)
3207f0337cdSDavid du Colombier 		return Eisopen;
3217f0337cdSDavid du Colombier 
3227f0337cdSDavid du Colombier 	rhdr.nwqid = 0;
3237f0337cdSDavid du Colombier 	nf = nil;
3247f0337cdSDavid du Colombier 	savedir = f->dir;
3257f0337cdSDavid du Colombier 	/* clone if requested */
3267f0337cdSDavid du Colombier 	if(thdr.newfid != thdr.fid){
3277f0337cdSDavid du Colombier 		nf = doclone(f, thdr.newfid);
3287f0337cdSDavid du Colombier 		if(nf == nil)
3297f0337cdSDavid du Colombier 			return "new fid in use";
3307f0337cdSDavid du Colombier 		f = nf;
3317f0337cdSDavid du Colombier 	}
3327f0337cdSDavid du Colombier 
3337f0337cdSDavid du Colombier 	/* if it's just a clone, return */
3347f0337cdSDavid du Colombier 	if(thdr.nwname == 0 && nf != nil)
3357f0337cdSDavid du Colombier 		return nil;
3367f0337cdSDavid du Colombier 
3377f0337cdSDavid du Colombier 	/* walk each element */
3387f0337cdSDavid du Colombier 	rv = nil;
3397f0337cdSDavid du Colombier 	for(i = 0; i < thdr.nwname; i++){
3407f0337cdSDavid du Colombier 		rv = dowalk(f, thdr.wname[i]);
3417f0337cdSDavid du Colombier 		if(rv != nil){
3427f0337cdSDavid du Colombier 			if(nf != nil)
3437f0337cdSDavid du Colombier 				rclunk(nf);
3447f0337cdSDavid du Colombier 			else
3457f0337cdSDavid du Colombier 				f->dir = savedir;
3467f0337cdSDavid du Colombier 			break;
3477f0337cdSDavid du Colombier 		}
3487f0337cdSDavid du Colombier 		rhdr.wqid[i] = f->dir->qid;
3497f0337cdSDavid du Colombier 	}
3507f0337cdSDavid du Colombier 	rhdr.nwqid = i;
3517f0337cdSDavid du Colombier 
3527f0337cdSDavid du Colombier 	/* we only error out if no walk  */
3537f0337cdSDavid du Colombier 	if(i > 0)
3547f0337cdSDavid du Colombier 		rv = nil;
3557f0337cdSDavid du Colombier 
3567f0337cdSDavid du Colombier 	return rv;
3577f0337cdSDavid du Colombier }
3587f0337cdSDavid du Colombier 
3597f0337cdSDavid du Colombier Audioctldata *
allocaudioctldata(void)3607f0337cdSDavid du Colombier allocaudioctldata(void)
3617f0337cdSDavid du Colombier {
3627f0337cdSDavid du Colombier 	int i, j, k;
3637f0337cdSDavid du Colombier 	Audioctldata *a;
3647f0337cdSDavid du Colombier 
3657f0337cdSDavid du Colombier 	a = emallocz(sizeof(Audioctldata), 1);
3667f0337cdSDavid du Colombier 	for(i = 0; i < 2; i++)
3677f0337cdSDavid du Colombier 		for(j=0; j < Ncontrol; j++)
368ce31847cSDavid du Colombier 			for(k=0; k < 8; k++)
3697f0337cdSDavid du Colombier 				a->values[i][j][k] = Undef;
3707f0337cdSDavid du Colombier 	return a;
3717f0337cdSDavid du Colombier }
3727f0337cdSDavid du Colombier 
3737f0337cdSDavid du Colombier char *
ropen(Fid * f)3747f0337cdSDavid du Colombier ropen(Fid *f)
3757f0337cdSDavid du Colombier {
3767f0337cdSDavid du Colombier 	if(f->flags & Open)
3777f0337cdSDavid du Colombier 		return Eisopen;
3787f0337cdSDavid du Colombier 
3795ab4dd4cSDavid du Colombier 	if(thdr.mode != OREAD && (f->dir->mode & 0x2) == 0)
3807f0337cdSDavid du Colombier 		return Eperm;
3817f0337cdSDavid du Colombier 	qlock(f);
3827f0337cdSDavid du Colombier 	if(f->dir == &dirs[Qaudioctl] && f->fiddata == nil)
3837f0337cdSDavid du Colombier 		f->fiddata = allocaudioctldata();
3847f0337cdSDavid du Colombier 	qunlock(f);
3857f0337cdSDavid du Colombier 	rhdr.iounit = 0;
3867f0337cdSDavid du Colombier 	rhdr.qid = f->dir->qid;
3877f0337cdSDavid du Colombier 	f->flags |= Open;
3887f0337cdSDavid du Colombier 	return nil;
3897f0337cdSDavid du Colombier }
3907f0337cdSDavid du Colombier 
3917f0337cdSDavid du Colombier char *
rcreate(Fid *)3927f0337cdSDavid du Colombier rcreate(Fid*)
3937f0337cdSDavid du Colombier {
3947f0337cdSDavid du Colombier 	return Eperm;
3957f0337cdSDavid du Colombier }
3967f0337cdSDavid du Colombier 
3977f0337cdSDavid du Colombier int
readtopdir(Fid *,uchar * buf,long off,int cnt,int blen)3987f0337cdSDavid du Colombier readtopdir(Fid*, uchar *buf, long off, int cnt, int blen)
3997f0337cdSDavid du Colombier {
4007f0337cdSDavid du Colombier 	int i, m, n;
4017f0337cdSDavid du Colombier 	long pos;
4027f0337cdSDavid du Colombier 
4037f0337cdSDavid du Colombier 	n = 0;
4047f0337cdSDavid du Colombier 	pos = 0;
4057f0337cdSDavid du Colombier 	for(i = 1; i < Nqid; i++){
4067f0337cdSDavid du Colombier 		m = convD2M(&dirs[i], &buf[n], blen-n);
4077f0337cdSDavid du Colombier 		if(off <= pos){
4087f0337cdSDavid du Colombier 			if(m <= BIT16SZ || m > cnt)
4097f0337cdSDavid du Colombier 				break;
4107f0337cdSDavid du Colombier 			n += m;
4117f0337cdSDavid du Colombier 			cnt -= m;
4127f0337cdSDavid du Colombier 		}
4137f0337cdSDavid du Colombier 		pos += m;
4147f0337cdSDavid du Colombier 	}
4157f0337cdSDavid du Colombier 	return n;
4167f0337cdSDavid du Colombier }
4177f0337cdSDavid du Colombier 
4182ddf2468SDavid du Colombier enum { Chunk = 1024, };
4192ddf2468SDavid du Colombier 
4207f0337cdSDavid du Colombier int
makeaudioctldata(Fid * f)4217f0337cdSDavid du Colombier makeaudioctldata(Fid *f)
4227f0337cdSDavid du Colombier {
4232ddf2468SDavid du Colombier 	int rec, ctl, i, diff;
4242ddf2468SDavid du Colombier 	long *actls;		/* 8 of them */
4257f0337cdSDavid du Colombier 	char *p, *e;
4267f0337cdSDavid du Colombier 	Audiocontrol *c;
4277f0337cdSDavid du Colombier 	Audioctldata *a;
4287f0337cdSDavid du Colombier 
4297f0337cdSDavid du Colombier 	if((a = f->fiddata) == nil)
4307f0337cdSDavid du Colombier 		sysfatal("fiddata");
4312ddf2468SDavid du Colombier 	if((p = a->s) == nil)
432906943f9SDavid du Colombier 		a->s = p = emallocz(Chunk, 0);
4332ddf2468SDavid du Colombier 	e = p + Chunk - 1;	/* e must point *at* last byte, not *after* */
4347f0337cdSDavid du Colombier 	for(rec = 0; rec < 2; rec++)
4357f0337cdSDavid du Colombier 		for(ctl = 0; ctl < Ncontrol; ctl++){
4367f0337cdSDavid du Colombier 			c = &controls[rec][ctl];
4372ddf2468SDavid du Colombier 			actls = a->values[rec][ctl];
4382ddf2468SDavid du Colombier 			diff = 0;
4397f0337cdSDavid du Colombier 			if(c->chans){
440ce31847cSDavid du Colombier 				for(i = 1; i < 8; i++)
4412ddf2468SDavid du Colombier 					if((c->chans & 1<<i) &&
4422ddf2468SDavid du Colombier 					    c->value[i] != actls[i])
4432ddf2468SDavid du Colombier 						diff = 1;
4447f0337cdSDavid du Colombier 			}else
4452ddf2468SDavid du Colombier 				if(c->value[0] != actls[0])
4462ddf2468SDavid du Colombier 					diff = 1;
4472ddf2468SDavid du Colombier 			if(diff){
4482ddf2468SDavid du Colombier 				p = seprint(p, e, "%s %s %A", c->name,
4492ddf2468SDavid du Colombier 					rec? "in": "out", c);
4502ddf2468SDavid du Colombier 				memmove(actls, c->value, sizeof c->value);
4517f0337cdSDavid du Colombier 				if(c->min != Undef){
4522ddf2468SDavid du Colombier 					p = seprint(p, e, " %ld %ld", c->min,
4532ddf2468SDavid du Colombier 						c->max);
4547f0337cdSDavid du Colombier 					if(c->step != Undef)
4552ddf2468SDavid du Colombier 						p = seprint(p, e, " %ld",
4562ddf2468SDavid du Colombier 							c->step);
4577f0337cdSDavid du Colombier 				}
4587f0337cdSDavid du Colombier 				p = seprint(p, e, "\n");
4597f0337cdSDavid du Colombier 			}
4607f0337cdSDavid du Colombier 		}
4612ddf2468SDavid du Colombier 	assert(strlen(a->s) < Chunk);
4627f0337cdSDavid du Colombier 	a->ns = p - a->s;
4637f0337cdSDavid du Colombier 	return a->ns;
4647f0337cdSDavid du Colombier }
4657f0337cdSDavid du Colombier 
4667f0337cdSDavid du Colombier void
readproc(void * x)4677f0337cdSDavid du Colombier readproc(void *x)
4687f0337cdSDavid du Colombier {
4697f0337cdSDavid du Colombier 	int n, cnt;
4707f0337cdSDavid du Colombier 	ulong event;
4712ddf2468SDavid du Colombier 	vlong off;
4722ddf2468SDavid du Colombier 	uchar *mdata;
4732ddf2468SDavid du Colombier 	Audioctldata *a;
4742ddf2468SDavid du Colombier 	Fcall *rhdr;
4752ddf2468SDavid du Colombier 	Fid *f;
4762ddf2468SDavid du Colombier 	Worker *w;
4777f0337cdSDavid du Colombier 
4787f0337cdSDavid du Colombier 	w = x;
479906943f9SDavid du Colombier 	mdata = emallocz(8*1024+IOHDRSZ, 0);
4807f0337cdSDavid du Colombier 	while(event = recvul(w->eventc)){
4817f0337cdSDavid du Colombier 		if(event != Work)
4827f0337cdSDavid du Colombier 			continue;
4837f0337cdSDavid du Colombier 		f = w->fid;
4847f0337cdSDavid du Colombier 		rhdr = w->rhdr;
4857f0337cdSDavid du Colombier 		a = f->fiddata;
4867f0337cdSDavid du Colombier 		off = rhdr->offset;
4877f0337cdSDavid du Colombier 		cnt = rhdr->count;
4887f0337cdSDavid du Colombier 		assert(a->offoff == off);
4897f0337cdSDavid du Colombier 		/* f is already locked */
4907f0337cdSDavid du Colombier 		for(;;){
4917f0337cdSDavid du Colombier 			qunlock(f);
4927f0337cdSDavid du Colombier 			event = recvul(w->eventc);
4937f0337cdSDavid du Colombier 			qlock(f);
494906943f9SDavid du Colombier 			ddprint(2, "readproc unblocked fid %d %lld\n",
4952ddf2468SDavid du Colombier 					f->fid, f->dir->qid.path);
4967f0337cdSDavid du Colombier 			switch (event & 0xffff){
4977f0337cdSDavid du Colombier 			case Work:
4987f0337cdSDavid du Colombier 				sysfatal("readproc phase error");
4997f0337cdSDavid du Colombier 			case Check:
5007f0337cdSDavid du Colombier 				if(f->fiddata && makeaudioctldata(f) == 0)
5017f0337cdSDavid du Colombier 					continue;
5027f0337cdSDavid du Colombier 				break;
5037f0337cdSDavid du Colombier 			case Flush:
5047f0337cdSDavid du Colombier 				if((event >> 16) == rhdr->tag){
505906943f9SDavid du Colombier 					ddprint(2, "readproc flushing fid %d, tag %d\n",
5062ddf2468SDavid du Colombier 						f->fid, rhdr->tag);
5077f0337cdSDavid du Colombier 					goto flush;
5087f0337cdSDavid du Colombier 				}
5097f0337cdSDavid du Colombier 				continue;
5107f0337cdSDavid du Colombier 			}
5117f0337cdSDavid du Colombier 			if(f->fiddata){
5127f0337cdSDavid du Colombier 				rhdr->data = a->s;
5137f0337cdSDavid du Colombier 				rhdr->count = a->ns;
5147f0337cdSDavid du Colombier 				break;
5157f0337cdSDavid du Colombier 			}
5167f0337cdSDavid du Colombier 			yield();
5177f0337cdSDavid du Colombier 		}
5187f0337cdSDavid du Colombier 		if(rhdr->count > cnt)
5197f0337cdSDavid du Colombier 			rhdr->count = cnt;
5207f0337cdSDavid du Colombier 		if(rhdr->count)
5217f0337cdSDavid du Colombier 			f->flags &= ~Eof;
522906943f9SDavid du Colombier 		ddprint(2, "readproc:->%F\n", rhdr);
5237f0337cdSDavid du Colombier 		n = convS2M(rhdr, mdata, messagesize);
5247f0337cdSDavid du Colombier 		if(write(mfd[1], mdata, n) != n)
5257f0337cdSDavid du Colombier 			sysfatal("mount write");
5267f0337cdSDavid du Colombier flush:
5277f0337cdSDavid du Colombier 		w->tag = NOTAG;
5287f0337cdSDavid du Colombier 		f->readers--;
5297f0337cdSDavid du Colombier 		assert(f->readers == 0);
5307f0337cdSDavid du Colombier 		free(rhdr);
5317f0337cdSDavid du Colombier 		w->rhdr = nil;
5327f0337cdSDavid du Colombier 		qunlock(f);
5337f0337cdSDavid du Colombier 		sendp(procchan, w);
5347f0337cdSDavid du Colombier 	}
5357f0337cdSDavid du Colombier 	threadexits(nil);
5367f0337cdSDavid du Colombier }
5377f0337cdSDavid du Colombier 
5387f0337cdSDavid du Colombier char*
rread(Fid * f)5397f0337cdSDavid du Colombier rread(Fid *f)
5407f0337cdSDavid du Colombier {
54145d5944cSDavid du Colombier 	int i, n, cnt, rec, div;
5422ddf2468SDavid du Colombier 	vlong off;
5437f0337cdSDavid du Colombier 	char *p;
5447f0337cdSDavid du Colombier 	Audiocontrol *c;
5457f0337cdSDavid du Colombier 	Audioctldata *a;
5467f0337cdSDavid du Colombier 	Worker *w;
5472ddf2468SDavid du Colombier 	static char buf[1024];
5487f0337cdSDavid du Colombier 
5497f0337cdSDavid du Colombier 	rhdr.count = 0;
5507f0337cdSDavid du Colombier 	off = thdr.offset;
5517f0337cdSDavid du Colombier 	cnt = thdr.count;
5527f0337cdSDavid du Colombier 
5537f0337cdSDavid du Colombier 	if(cnt > messagesize - IOHDRSZ)
5547f0337cdSDavid du Colombier 		cnt = messagesize - IOHDRSZ;
5557f0337cdSDavid du Colombier 
5567f0337cdSDavid du Colombier 	rhdr.data = (char*)mbuf;
5577f0337cdSDavid du Colombier 
5587f0337cdSDavid du Colombier 	if(f->dir == &dirs[Qdir]){
5597f0337cdSDavid du Colombier 		n = readtopdir(f, mbuf, off, cnt, messagesize - IOHDRSZ);
5607f0337cdSDavid du Colombier 		rhdr.count = n;
5617f0337cdSDavid du Colombier 		return nil;
5627f0337cdSDavid du Colombier 	}
5637f0337cdSDavid du Colombier 
5647f0337cdSDavid du Colombier 	if(f->dir == &dirs[Qvolume]){
5657f0337cdSDavid du Colombier 		p = buf;
5667f0337cdSDavid du Colombier 		n = sizeof buf;
5677f0337cdSDavid du Colombier 		for(rec = 0; rec < 2; rec++){
5687f0337cdSDavid du Colombier 			c = &controls[rec][Volume_control];
5697f0337cdSDavid du Colombier 			if(c->readable){
57045d5944cSDavid du Colombier 				div = c->max - c->min;
5712ddf2468SDavid du Colombier 				i = snprint(p, n, "audio %s %ld\n",
5722ddf2468SDavid du Colombier 					rec? "in": "out", (c->min != Undef?
57345d5944cSDavid du Colombier 					100*(c->value[0]-c->min)/(div? div: 1):
5742ddf2468SDavid du Colombier 					c->value[0]));
5752ddf2468SDavid du Colombier 				p += i;
5762ddf2468SDavid du Colombier 				n -= i;
5777f0337cdSDavid du Colombier 			}
5787f0337cdSDavid du Colombier 			c = &controls[rec][Treble_control];
5797f0337cdSDavid du Colombier 			if(c->readable){
58045d5944cSDavid du Colombier 				div = c->max - c->min;
5812ddf2468SDavid du Colombier 				i = snprint(p, n, "treb %s %ld\n",
5822ddf2468SDavid du Colombier 					rec? "in": "out", (c->min != Undef?
58345d5944cSDavid du Colombier 					100*(c->value[0]-c->min)/(div? div: 1):
5842ddf2468SDavid du Colombier 					c->value[0]));
5852ddf2468SDavid du Colombier 				p += i;
5862ddf2468SDavid du Colombier 				n -= i;
5877f0337cdSDavid du Colombier 			}
5887f0337cdSDavid du Colombier 			c = &controls[rec][Bass_control];
5897f0337cdSDavid du Colombier 			if(c->readable){
59045d5944cSDavid du Colombier 				div = c->max - c->min;
5912ddf2468SDavid du Colombier 				i = snprint(p, n, "bass %s %ld\n",
5922ddf2468SDavid du Colombier 					rec? "in": "out", (c->min != Undef?
59345d5944cSDavid du Colombier 					100*(c->value[0]-c->min)/(div? div: 1):
5942ddf2468SDavid du Colombier 					c->value[0]));
5952ddf2468SDavid du Colombier 				p += i;
5962ddf2468SDavid du Colombier 				n -= i;
5977f0337cdSDavid du Colombier 			}
5987f0337cdSDavid du Colombier 			c = &controls[rec][Speed_control];
5997f0337cdSDavid du Colombier 			if(c->readable){
6002ddf2468SDavid du Colombier 				i = snprint(p, n, "speed %s %ld\n",
6012ddf2468SDavid du Colombier 					rec? "in": "out", c->value[0]);
6022ddf2468SDavid du Colombier 				p += i;
6032ddf2468SDavid du Colombier 				n -= i;
6047f0337cdSDavid du Colombier 			}
6057f0337cdSDavid du Colombier 		}
6067f0337cdSDavid du Colombier 		n = sizeof buf - n;
6077f0337cdSDavid du Colombier 		if(off > n)
6087f0337cdSDavid du Colombier 			rhdr.count = 0;
6097f0337cdSDavid du Colombier 		else{
6107f0337cdSDavid du Colombier 			rhdr.data = buf + off;
6117f0337cdSDavid du Colombier 			rhdr.count = n - off;
6127f0337cdSDavid du Colombier 			if(rhdr.count > cnt)
6137f0337cdSDavid du Colombier 				rhdr.count = cnt;
6147f0337cdSDavid du Colombier 		}
6157f0337cdSDavid du Colombier 		return nil;
6167f0337cdSDavid du Colombier 	}
6177f0337cdSDavid du Colombier 
6187f0337cdSDavid du Colombier 	if(f->dir == &dirs[Qaudioctl]){
6197f0337cdSDavid du Colombier 		Fcall *hdr;
6207f0337cdSDavid du Colombier 
6217f0337cdSDavid du Colombier 		qlock(f);
6227f0337cdSDavid du Colombier 		a = f->fiddata;
6237f0337cdSDavid du Colombier 		if(off - a->offoff < 0){
6247f0337cdSDavid du Colombier 			/* there was a seek */
6257f0337cdSDavid du Colombier 			a->offoff = off;
6267f0337cdSDavid du Colombier 			a->ns = 0;
6277f0337cdSDavid du Colombier 		}
6287f0337cdSDavid du Colombier 		do {
6297f0337cdSDavid du Colombier 			if(off - a->offoff < a->ns){
6307f0337cdSDavid du Colombier 				rhdr.data = a->s + (off - a->offoff);
6317f0337cdSDavid du Colombier 				rhdr.count = a->ns - (off - a->offoff);
6327f0337cdSDavid du Colombier 				if(rhdr.count > cnt)
6337f0337cdSDavid du Colombier 					rhdr.count = cnt;
6347f0337cdSDavid du Colombier 				qunlock(f);
6357f0337cdSDavid du Colombier 				return nil;
6367f0337cdSDavid du Colombier 			}
6377f0337cdSDavid du Colombier 			if(a->offoff != off){
6387f0337cdSDavid du Colombier 				a->ns = 0;
6397f0337cdSDavid du Colombier 				a->offoff = off;
6407f0337cdSDavid du Colombier 				rhdr.count = 0;
6417f0337cdSDavid du Colombier 				qunlock(f);
6427f0337cdSDavid du Colombier 				return nil;
6437f0337cdSDavid du Colombier 			}
6447f0337cdSDavid du Colombier 		} while(makeaudioctldata(f) != 0);
6457f0337cdSDavid du Colombier 
6467f0337cdSDavid du Colombier 		assert(a->offoff == off);
6477f0337cdSDavid du Colombier 		/* Wait for data off line */
6487f0337cdSDavid du Colombier 		f->readers++;
6497f0337cdSDavid du Colombier 		w = nbrecvp(procchan);
6507f0337cdSDavid du Colombier 		if(w == nil){
6517f0337cdSDavid du Colombier 			w = emallocz(sizeof(Worker), 1);
6527f0337cdSDavid du Colombier 			w->eventc = chancreate(sizeof(ulong), 1);
6537f0337cdSDavid du Colombier 			w->next = workers;
6547f0337cdSDavid du Colombier 			workers = w;
6557f0337cdSDavid du Colombier 			proccreate(readproc, w, 4096);
6567f0337cdSDavid du Colombier 		}
657906943f9SDavid du Colombier 		hdr = emallocz(sizeof(Fcall), 0);
6587f0337cdSDavid du Colombier 		w->fid = f;
6597f0337cdSDavid du Colombier 		w->tag = thdr.tag;
6607f0337cdSDavid du Colombier 		assert(w->rhdr == nil);
6617f0337cdSDavid du Colombier 		w->rhdr = hdr;
6627f0337cdSDavid du Colombier 		hdr->count = cnt;
6637f0337cdSDavid du Colombier 		hdr->offset = off;
6647f0337cdSDavid du Colombier 		hdr->type = thdr.type+1;
6657f0337cdSDavid du Colombier 		hdr->fid = thdr.fid;
6667f0337cdSDavid du Colombier 		hdr->tag = thdr.tag;
6677f0337cdSDavid du Colombier 		sendul(w->eventc, Work);
6687f0337cdSDavid du Colombier 		return (char*)~0;
6697f0337cdSDavid du Colombier 	}
6707f0337cdSDavid du Colombier 
6717f0337cdSDavid du Colombier 	return Eperm;
6727f0337cdSDavid du Colombier }
6737f0337cdSDavid du Colombier 
6747f0337cdSDavid du Colombier char*
rwrite(Fid * f)6757f0337cdSDavid du Colombier rwrite(Fid *f)
6767f0337cdSDavid du Colombier {
6777f0337cdSDavid du Colombier 	long cnt, value;
6787f0337cdSDavid du Colombier 	char *lines[2*Ncontrol], *fields[4], *subfields[9], *err, *p;
6797f0337cdSDavid du Colombier 	int nlines, i, nf, nnf, rec, ctl;
6807f0337cdSDavid du Colombier 	Audiocontrol *c;
6817f0337cdSDavid du Colombier 	Worker *w;
6827f0337cdSDavid du Colombier 	static char buf[256];
6837f0337cdSDavid du Colombier 
6847f0337cdSDavid du Colombier 	rhdr.count = 0;
6857f0337cdSDavid du Colombier 	cnt = thdr.count;
6867f0337cdSDavid du Colombier 
6877f0337cdSDavid du Colombier 	if(cnt > messagesize - IOHDRSZ)
6887f0337cdSDavid du Colombier 		cnt = messagesize - IOHDRSZ;
6897f0337cdSDavid du Colombier 
6907f0337cdSDavid du Colombier 	err = nil;
6917f0337cdSDavid du Colombier 	if(f->dir == &dirs[Qvolume] || f->dir == &dirs[Qaudioctl]){
6927f0337cdSDavid du Colombier 		thdr.data[cnt] = '\0';
6937f0337cdSDavid du Colombier 		nlines = getfields(thdr.data, lines, 2*Ncontrol, 1, "\n");
6947f0337cdSDavid du Colombier 		for(i = 0; i < nlines; i++){
695906943f9SDavid du Colombier 			dprint(2, "line: %s\n", lines[i]);
6967f0337cdSDavid du Colombier 			nf = tokenize(lines[i], fields, 4);
6977f0337cdSDavid du Colombier 			if(nf == 0)
6987f0337cdSDavid du Colombier 				continue;
6992ddf2468SDavid du Colombier 			if(nf == 3)
7002ddf2468SDavid du Colombier 				if(strcmp(fields[1], "in") == 0 ||
7012ddf2468SDavid du Colombier 				    strcmp(fields[1], "record") == 0)
7027f0337cdSDavid du Colombier 					rec = 1;
7032ddf2468SDavid du Colombier 				else if(strcmp(fields[1], "out") == 0 ||
7042ddf2468SDavid du Colombier 				    strcmp(fields[1], "playback") == 0)
7057f0337cdSDavid du Colombier 					rec = 0;
7067f0337cdSDavid du Colombier 				else{
707906943f9SDavid du Colombier 					dprint(2, "bad1\n");
7087f0337cdSDavid du Colombier 					return Ebadctl;
7097f0337cdSDavid du Colombier 				}
7102ddf2468SDavid du Colombier 			else if(nf == 2)
7117f0337cdSDavid du Colombier 				rec = 0;
7127f0337cdSDavid du Colombier 			else{
713906943f9SDavid du Colombier 				dprint(2, "bad2 %d\n", nf);
7147f0337cdSDavid du Colombier 				return Ebadctl;
7157f0337cdSDavid du Colombier 			}
7167f0337cdSDavid du Colombier 			c = nil;
7177f0337cdSDavid du Colombier 			if(strcmp(fields[0], "audio") == 0) /* special case */
7187f0337cdSDavid du Colombier 				fields[0] = "volume";
7197f0337cdSDavid du Colombier 			for(ctl = 0; ctl < Ncontrol; ctl++){
7207f0337cdSDavid du Colombier 				c = &controls[rec][ctl];
7217f0337cdSDavid du Colombier 				if(strcmp(fields[0], c->name) == 0)
7227f0337cdSDavid du Colombier 					break;
7237f0337cdSDavid du Colombier 			}
7247f0337cdSDavid du Colombier 			if(ctl == Ncontrol){
725906943f9SDavid du Colombier 				dprint(2, "bad3\n");
7267f0337cdSDavid du Colombier 				return Ebadctl;
7277f0337cdSDavid du Colombier 			}
7282ddf2468SDavid du Colombier 			if(f->dir == &dirs[Qvolume] && ctl != Speed_control &&
7292ddf2468SDavid du Colombier 			    c->min != Undef && c->max != Undef){
7302ddf2468SDavid du Colombier 				nnf = tokenize(fields[nf-1], subfields,
7312ddf2468SDavid du Colombier 					nelem(subfields));
7327f0337cdSDavid du Colombier 				if(nnf <= 0 || nnf > 8){
733906943f9SDavid du Colombier 					dprint(2, "bad4\n");
7347f0337cdSDavid du Colombier 					return Ebadctl;
7357f0337cdSDavid du Colombier 				}
7367f0337cdSDavid du Colombier 				p = buf;
7377f0337cdSDavid du Colombier 				for(i = 0; i < nnf; i++){
7387f0337cdSDavid du Colombier 					value = strtol(subfields[i], nil, 0);
7392ddf2468SDavid du Colombier 					value = ((100 - value)*c->min +
7402ddf2468SDavid du Colombier 						value*c->max) / 100;
741906943f9SDavid du Colombier 					if(p == buf){
742906943f9SDavid du Colombier 						dprint(2, "rwrite: %s %s '%ld",
7432ddf2468SDavid du Colombier 								c->name, rec?
7442ddf2468SDavid du Colombier 								"record":
7452ddf2468SDavid du Colombier 								"playback",
7462ddf2468SDavid du Colombier 								value);
747906943f9SDavid du Colombier 					}else
748906943f9SDavid du Colombier 						dprint(2, " %ld", value);
7497f0337cdSDavid du Colombier 					if(p == buf)
7502ddf2468SDavid du Colombier 						p = seprint(p, buf+sizeof buf,
7512ddf2468SDavid du Colombier 							"0x%p %s %s '%ld",
7522ddf2468SDavid du Colombier 							replchan, c->name, rec?
7532ddf2468SDavid du Colombier 							"record": "playback",
7542ddf2468SDavid du Colombier 							value);
7557f0337cdSDavid du Colombier 					else
7562ddf2468SDavid du Colombier 						p = seprint(p, buf+sizeof buf,
7572ddf2468SDavid du Colombier 							" %ld", value);
7587f0337cdSDavid du Colombier 				}
759906943f9SDavid du Colombier 				dprint(2, "'\n");
7602ddf2468SDavid du Colombier 				seprint(p, buf+sizeof buf-1, "'");
7617f0337cdSDavid du Colombier 				chanprint(controlchan, buf);
7627f0337cdSDavid du Colombier 			}else{
763906943f9SDavid du Colombier 				dprint(2, "rwrite: %s %s %q", c->name,
7642ddf2468SDavid du Colombier 						rec? "record": "playback",
7652ddf2468SDavid du Colombier 						fields[nf-1]);
7662ddf2468SDavid du Colombier 				chanprint(controlchan, "0x%p %s %s %q",
7672ddf2468SDavid du Colombier 					replchan, c->name, rec? "record":
7682ddf2468SDavid du Colombier 					"playback", fields[nf-1]);
7697f0337cdSDavid du Colombier 			}
7707f0337cdSDavid du Colombier 			p = recvp(replchan);
7717f0337cdSDavid du Colombier 			if(p){
7727f0337cdSDavid du Colombier 				if(strcmp(p, "ok") == 0){
7737f0337cdSDavid du Colombier 					free(p);
7747f0337cdSDavid du Colombier 					p = nil;
7757f0337cdSDavid du Colombier 				}
7767f0337cdSDavid du Colombier 				if(err == nil)
7777f0337cdSDavid du Colombier 					err = p;
7787f0337cdSDavid du Colombier 			}
7797f0337cdSDavid du Colombier 		}
7807f0337cdSDavid du Colombier 		for(w = workers; w; w = w->next)
7817f0337cdSDavid du Colombier 			nbsendul(w->eventc, Qaudioctl << 16 | Check);
7827f0337cdSDavid du Colombier 		rhdr.count = thdr.count;
7837f0337cdSDavid du Colombier 		return err;
7847f0337cdSDavid du Colombier 	}
7857f0337cdSDavid du Colombier 	return Eperm;
7867f0337cdSDavid du Colombier }
7877f0337cdSDavid du Colombier 
7887f0337cdSDavid du Colombier char *
rclunk(Fid * f)7897f0337cdSDavid du Colombier rclunk(Fid *f)
7907f0337cdSDavid du Colombier {
7917f0337cdSDavid du Colombier 	Audioctldata *a;
7927f0337cdSDavid du Colombier 
7937f0337cdSDavid du Colombier 	qlock(f);
7947f0337cdSDavid du Colombier 	f->flags &= ~(Open|Busy);
7957f0337cdSDavid du Colombier 	assert(f->readers ==0);
7967f0337cdSDavid du Colombier 	if(f->fiddata){
7977f0337cdSDavid du Colombier 		a = f->fiddata;
7987f0337cdSDavid du Colombier 		if(a->s)
7997f0337cdSDavid du Colombier 			free(a->s);
8007f0337cdSDavid du Colombier 		free(a);
8017f0337cdSDavid du Colombier 		f->fiddata = nil;
8027f0337cdSDavid du Colombier 	}
8037f0337cdSDavid du Colombier 	qunlock(f);
8047f0337cdSDavid du Colombier 	return 0;
8057f0337cdSDavid du Colombier }
8067f0337cdSDavid du Colombier 
8077f0337cdSDavid du Colombier char *
rremove(Fid *)8087f0337cdSDavid du Colombier rremove(Fid *)
8097f0337cdSDavid du Colombier {
8107f0337cdSDavid du Colombier 	return Eperm;
8117f0337cdSDavid du Colombier }
8127f0337cdSDavid du Colombier 
8137f0337cdSDavid du Colombier char *
rstat(Fid * f)8147f0337cdSDavid du Colombier rstat(Fid *f)
8157f0337cdSDavid du Colombier {
8167f0337cdSDavid du Colombier 	Audioctldata *a;
8177f0337cdSDavid du Colombier 
8187f0337cdSDavid du Colombier 	if(f->dir == &dirs[Qaudioctl]){
8197f0337cdSDavid du Colombier 		qlock(f);
8207f0337cdSDavid du Colombier 		if(f->fiddata == nil)
8217f0337cdSDavid du Colombier 			f->fiddata = allocaudioctldata();
8227f0337cdSDavid du Colombier 		a = f->fiddata;
8237f0337cdSDavid du Colombier 		if(a->ns == 0)
8247f0337cdSDavid du Colombier 			makeaudioctldata(f);
8257f0337cdSDavid du Colombier 		f->dir->length = a->offoff + a->ns;
8267f0337cdSDavid du Colombier 		qunlock(f);
8277f0337cdSDavid du Colombier 	}
8287f0337cdSDavid du Colombier 	rhdr.nstat = convD2M(f->dir, mbuf, messagesize - IOHDRSZ);
8297f0337cdSDavid du Colombier 	rhdr.stat = mbuf;
8307f0337cdSDavid du Colombier 	return 0;
8317f0337cdSDavid du Colombier }
8327f0337cdSDavid du Colombier 
8337f0337cdSDavid du Colombier char *
rwstat(Fid *)8347f0337cdSDavid du Colombier rwstat(Fid*)
8357f0337cdSDavid du Colombier {
8367f0337cdSDavid du Colombier 	return Eperm;
8377f0337cdSDavid du Colombier }
8387f0337cdSDavid du Colombier 
8397f0337cdSDavid du Colombier Fid *
newfid(int fid)8407f0337cdSDavid du Colombier newfid(int fid)
8417f0337cdSDavid du Colombier {
8427f0337cdSDavid du Colombier 	Fid *f, *ff;
8437f0337cdSDavid du Colombier 
8447f0337cdSDavid du Colombier 	ff = nil;
8457f0337cdSDavid du Colombier 	for(f = fids; f; f = f->next)
8462ddf2468SDavid du Colombier 		if(f->fid == fid)
8477f0337cdSDavid du Colombier 			return f;
8482ddf2468SDavid du Colombier 		else if(ff == nil && (f->flags & Busy) == 0)
8497f0337cdSDavid du Colombier 			ff = f;
8507f0337cdSDavid du Colombier 	if(ff == nil){
8517f0337cdSDavid du Colombier 		ff = emallocz(sizeof *ff, 1);
8527f0337cdSDavid du Colombier 		ff->next = fids;
8537f0337cdSDavid du Colombier 		fids = ff;
8547f0337cdSDavid du Colombier 	}
8557f0337cdSDavid du Colombier 	ff->fid = fid;
8567f0337cdSDavid du Colombier 	ff->flags &= ~(Busy|Open);
8577f0337cdSDavid du Colombier 	ff->dir = nil;
8587f0337cdSDavid du Colombier 	return ff;
8597f0337cdSDavid du Colombier }
8607f0337cdSDavid du Colombier 
8617f0337cdSDavid du Colombier void
io(void *)8627f0337cdSDavid du Colombier io(void *)
8637f0337cdSDavid du Colombier {
8647f0337cdSDavid du Colombier 	char *err, e[32];
8657f0337cdSDavid du Colombier 	int n;
8667f0337cdSDavid du Colombier 
8677f0337cdSDavid du Colombier 	close(p[1]);
8687f0337cdSDavid du Colombier 
8697f0337cdSDavid du Colombier 	procchan = chancreate(sizeof(Channel*), 8);
8707f0337cdSDavid du Colombier 	replchan = chancreate(sizeof(char*), 0);
8717f0337cdSDavid du Colombier 	for(;;){
8727f0337cdSDavid du Colombier 		/*
8737f0337cdSDavid du Colombier 		 * reading from a pipe or a network device
8747f0337cdSDavid du Colombier 		 * will give an error after a few eof reads
8757f0337cdSDavid du Colombier 		 * however, we cannot tell the difference
8767f0337cdSDavid du Colombier 		 * between a zero-length read and an interrupt
8777f0337cdSDavid du Colombier 		 * on the processes writing to us,
8787f0337cdSDavid du Colombier 		 * so we wait for the error
8797f0337cdSDavid du Colombier 		 */
8807f0337cdSDavid du Colombier 		n = read9pmsg(mfd[0], mdata, messagesize);
8817f0337cdSDavid du Colombier 		if(n == 0)
8827f0337cdSDavid du Colombier 			continue;
8837f0337cdSDavid du Colombier 		if(n < 0){
8847f0337cdSDavid du Colombier 			rerrstr(e, sizeof e);
8857f0337cdSDavid du Colombier 			if(strcmp(e, "interrupted") == 0){
886906943f9SDavid du Colombier 				dprint(2, "read9pmsg interrupted\n");
8877f0337cdSDavid du Colombier 				continue;
8887f0337cdSDavid du Colombier 			}
8897f0337cdSDavid du Colombier 			return;
8907f0337cdSDavid du Colombier 		}
8917f0337cdSDavid du Colombier 		if(convM2S(mdata, n, &thdr) == 0)
8927f0337cdSDavid du Colombier 			continue;
8937f0337cdSDavid du Colombier 
894906943f9SDavid du Colombier 		ddprint(2, "io:<-%F\n", &thdr);
8957f0337cdSDavid du Colombier 
8967f0337cdSDavid du Colombier 		rhdr.data = (char*)mdata + messagesize;
8977f0337cdSDavid du Colombier 		if(!fcalls[thdr.type])
8987f0337cdSDavid du Colombier 			err = "bad fcall type";
8997f0337cdSDavid du Colombier 		else
9007f0337cdSDavid du Colombier 			err = (*fcalls[thdr.type])(newfid(thdr.fid));
9017f0337cdSDavid du Colombier 		if(err == (char*)~0)
9027f0337cdSDavid du Colombier 			continue;	/* handled off line */
9037f0337cdSDavid du Colombier 		if(err){
9047f0337cdSDavid du Colombier 			rhdr.type = Rerror;
9057f0337cdSDavid du Colombier 			rhdr.ename = err;
9067f0337cdSDavid du Colombier 		}else{
9077f0337cdSDavid du Colombier 			rhdr.type = thdr.type + 1;
9087f0337cdSDavid du Colombier 			rhdr.fid = thdr.fid;
9097f0337cdSDavid du Colombier 		}
9107f0337cdSDavid du Colombier 		rhdr.tag = thdr.tag;
911906943f9SDavid du Colombier 		ddprint(2, "io:->%F\n", &rhdr);
9127f0337cdSDavid du Colombier 		n = convS2M(&rhdr, mdata, messagesize);
9137f0337cdSDavid du Colombier 		if(write(mfd[1], mdata, n) != n)
9147f0337cdSDavid du Colombier 			sysfatal("mount write");
9157f0337cdSDavid du Colombier 	}
9167f0337cdSDavid du Colombier }
9177f0337cdSDavid du Colombier 
9187f0337cdSDavid du Colombier int
newid(void)9197f0337cdSDavid du Colombier newid(void)
9207f0337cdSDavid du Colombier {
9217f0337cdSDavid du Colombier 	int rv;
9227f0337cdSDavid du Colombier 	static int id;
9237f0337cdSDavid du Colombier 	static Lock idlock;
9247f0337cdSDavid du Colombier 
9257f0337cdSDavid du Colombier 	lock(&idlock);
9267f0337cdSDavid du Colombier 	rv = ++id;
9277f0337cdSDavid du Colombier 	unlock(&idlock);
9287f0337cdSDavid du Colombier 
9297f0337cdSDavid du Colombier 	return rv;
9307f0337cdSDavid du Colombier }
9317f0337cdSDavid du Colombier 
9327f0337cdSDavid du Colombier void
ctlevent(void)9337f0337cdSDavid du Colombier ctlevent(void)
9347f0337cdSDavid du Colombier {
9357f0337cdSDavid du Colombier 	Worker *w;
9367f0337cdSDavid du Colombier 
9377f0337cdSDavid du Colombier 	for(w = workers; w; w = w->next)
9387f0337cdSDavid du Colombier 		nbsendul(w->eventc, Qaudioctl << 16 | Check);
9397f0337cdSDavid du Colombier }
940