git: 9front

ref: 902bd7ba274c50743a2cb15f4bca7a55b443172e
dir: /sys/src/9/port/devaudio.c/

View raw version
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"io.h"
#include	"../port/error.h"

typedef struct Audioprobe Audioprobe;
struct Audioprobe {
	char *name;
	int (*probe)(Audio*);
};

enum {
	Qdir = 0,
	Qaudio,
	Qaudioctl,
	Qaudiostatus,
	Qvolume,
	Maxaudioprobes = 8,
};

static int naudioprobes;
static Audioprobe audioprobes[Maxaudioprobes];
static Audio *audiodevs;

static Dirtab audiodir[] = {
	".",	{Qdir, 0, QTDIR},	0,	DMDIR|0555,
	"audio",	{Qaudio},			0,	0666,
	"audioctl",	{Qaudioctl},			0,	0666,
	"audiostat",	{Qaudiostatus},			0,	0666,
	"volume",	{Qvolume},			0,	0666,
};

void
addaudiocard(char *name, int (*probefn)(Audio *))
{
	Audioprobe *probe;
	probe = &audioprobes[naudioprobes++];
	probe->name = name;
	probe->probe = probefn;
}

static void
audioreset(void)
{
	int i, ctlrno = 0;
	Audio **pp;
	Audioprobe *probe;
	pp = &audiodevs;
	*pp = malloc(sizeof(Audio));
	(*pp)->ctlrno = ctlrno++;
	for(i = 0; i < naudioprobes; i++){
		probe = &audioprobes[i];
		(*pp)->name = probe->name;
		while(!probe->probe(*pp)){
			pp = &(*pp)->next;
			*pp = malloc(sizeof(Audio));
			(*pp)->ctlrno = ctlrno++;
			(*pp)->name = probe->name;
		}
	}
	free(*pp);
	*pp = nil;
}

static Chan*
audioattach(char *spec)
{
	Chan *c;
	Audio *p;
	int i;
	if(spec != nil && *spec != '\0')
		i = strtol(spec, 0, 10);
	else
		i = 0;
	for(p = audiodevs; p; p = p->next)
		if(i-- == 0)
			break;
	if(p == nil)
		error(Enodev);
	c = devattach('A', spec);
	c->qid.path = Qdir;
	c->aux = p;
	if(p->attach)
		p->attach(p);
	return c;
}

static long
audioread(Chan *c, void *a, long n, vlong off)
{
	Audio *adev;
	long (*fn)(Audio *, void *, long, vlong);
	adev = c->aux;
	switch((ulong)c->qid.path){
	default:
		error("audio bugger (rd)");
	case Qaudioctl:
		fn = adev->ctl;
		break;
	case Qdir:
		if(adev->buffered)
			audiodir[Qaudio].length = adev->buffered(adev);
		return devdirread(c, a, n, audiodir, nelem(audiodir), devgen);
	case Qaudio:
		fn = adev->read;
		break;
	case Qaudiostatus:
		fn = adev->status;
		break;
	case Qvolume:
		fn = adev->volread;
		break;
	}
	if(fn == nil)
		error("not implemented");
	return fn(adev, a, n, off);
}

static long
audiowrite(Chan *c, void *a, long n, vlong off)
{
	Audio *adev;
	long (*fn)(Audio *, void *, long, vlong);
	adev = c->aux;
	switch((ulong)c->qid.path){
	default:
		error("audio bugger (wr)");
	case Qaudio:
		fn = adev->write;
		break;
	case Qaudioctl:
		fn = adev->ctl;
		break;
	case Qvolume:
		fn = adev->volwrite;
		break;
	}
	if(fn == nil)
		error("not implemented");
	return fn(adev, a, n, off);
}

static void
audioclose(Chan *c)
{
	Audio *adev;
	adev = c->aux;
	switch((ulong)c->qid.path){
	default:
		return;
	case Qaudio:
		if(adev->close == nil)
			return;
		adev->close(adev);
		return;
	}
}

static Walkqid*
audiowalk(Chan *c, Chan *nc, char **name, int nname)
{
	return devwalk(c, nc, name, nname, audiodir, nelem(audiodir), devgen);
}

static int
audiostat(Chan *c, uchar *dp, int n)
{
	Audio *adev;
	adev = c->aux;
	if(adev->buffered && (ulong)c->qid.path == Qaudio)
		audiodir[Qaudio].length = adev->buffered(adev);
	return devstat(c, dp, n, audiodir, nelem(audiodir), devgen);
}

static Chan*
audioopen(Chan *c, int omode)
{
	return devopen(c, omode, audiodir, nelem(audiodir), devgen);
}

Dev audiodevtab = {
	'A',
	"audio",
	audioreset,
	devinit,
	devshutdown,
	audioattach,
	audiowalk,
	audiostat,
	audioopen,
	devcreate,
	audioclose,
	audioread,
	devbread,
	audiowrite,
	devbwrite,
	devremove,
	devwstat,
};