plan 9 kernel history: overview | file list | diff list

2000/1116/bitsy/devuda1341.c (diff list | history)

bitsy/devuda1341.c on 2000/1103
2000/1103    
/* 
 *	SAC/UDA 1341 Audio driver for the Bitsy 
 * 
 *	The Philips UDA 1341 sound chip is accessed through the Serial Audio 
 *	Controller (SAC) of the StrongARM SA-1110.  This is much more a SAC 
 *	controller than a UDA controller, but we have a devsac.c already. 
 * 
 *	The code morphs Nicolas Pitre's <nico@cam.org> Linux controller 
 *	and Ken's Soundblaster controller. 
 * 
 *	The interface should be identical to that of devaudio.c 
 */ 
#include	"u.h" 
#include	"../port/lib.h" 
#include	"mem.h" 
#include	"dat.h" 
#include	"fns.h" 
#include	"../port/error.h" 
#include	"io.h" 
2000/1110    
#include	"sa1110dma.h" 
2000/1103    
 
2000/1113    
static int debug = 1; 
 
2000/1103    
/* 
 * GPIO based L3 bus support. 
 * 
 * This provides control of Philips L3 type devices.  
 * GPIO lines are used for clock, data and mode pins. 
 * 
 * Note: The L3 pins are shared with I2C devices. This should not present 
 * any problems as long as an I2C start sequence is not generated. This is 
 * defined as a 1->0 transition on the data lines when the clock is high. 
 * It is critical this code only allow data transitions when the clock 
 * is low. This is always legal in L3. 
 * 
 * The IIC interface requires the clock and data pin to be LOW when idle. We 
 * must make sure we leave them in this state. 
 * 
 * It appears the read data is generated on the falling edge of the clock 
 * and should be held stable during the clock high time. 
 */ 
 
/*  
 * L3 setup and hold times (expressed in us) 
 */ 
#define L3_DataSetupTime	1		/* 190 ns */ 
#define L3_DataHoldTime		1		/*  30 ns */ 
#define L3_ModeSetupTime	1		/* 190 ns */ 
#define L3_ModeHoldTime		1		/* 190 ns */ 
#define L3_ClockHighTime	100		/* 250 ns (min is 64*fs, 35us @ 44.1 Khz) */ 
#define L3_ClockLowTime		100		/* 250 ns (min is 64*fs, 35us @ 44.1 Khz) */ 
#define L3_HaltTime			1		/* 190 ns */ 
 
/* UDA 1341 Registers */ 
enum { 
	/* Status0 register */ 
	UdaStatusDC		= 0,	/* 1 bit */ 
	UdaStatusIF		= 1,	/* 3 bits */ 
	UdaStatusSC		= 4,	/* 2 bits */ 
	UdaStatusRST	= 6,	/* 1 bit */ 
}; 
 
enum { 
	/* Status1 register */ 
	UdaStatusPC		= 0,	/* 2 bits */ 
	UdaStatusDS		= 2,	/* 1 bit */ 
	UdaStatusPDA	= 3,	/* 1 bit */ 
	UdaStatusPAD	= 4,	/* 1 bit */ 
	UdaStatusIGS	= 5,	/* 1 bit */ 
	UdaStatusOGS	= 6,	/* 1 bit */ 
}; 
 
/* 
 * UDA1341 L3 address and command types 
 */ 
#define UDA1341_DATA0	0 
#define UDA1341_DATA1	1 
#define UDA1341_STATUS	2 
2000/1111    
#define UDA1341_L3Addr	5 
2000/1103    
 
typedef struct	AQueue	AQueue; 
typedef struct	Buf	Buf; 
2000/1109    
typedef struct	IOstate IOstate; 
2000/1103    
 
enum 
{ 
	Qdir		= 0, 
	Qaudio, 
	Qvolume, 
	Qstatus, 
 
	Fmono		= 1, 
	Fin		= 2, 
	Fout		= 4, 
 
	Aclosed		= 0, 
	Aread, 
	Awrite, 
 
	Vaudio		= 0, 
	Vsynth, 
	Vcd, 
	Vline, 
	Vmic, 
	Vspeaker, 
	Vtreb, 
	Vbass, 
	Vspeed, 
	Nvol, 
 
2000/1109    
	Bufsize		= 8*1024,	/* 46 ms each */ 
	Nbuf		= 32,		/* 1.5 seconds total */ 
2000/1103    
 
	Speed		= 44100, 
	Ncmd		= 50,		/* max volume command words */ 
}; 
 
Dirtab 
audiodir[] = 
{ 
	"audio",	{Qaudio},		0,	0666, 
	"volume",	{Qvolume},		0,	0666, 
	"audiostat",{Qstatus},		0,	0444, 
}; 
 
struct	Buf 
{ 
	uchar*	virt; 
2000/1109    
	uint	nbytes; 
2000/1103    
}; 
2000/1104    
 
2000/1109    
struct	IOstate 
2000/1103    
{ 
	QLock; 
2000/1115    
	Lock			ilock; 
2000/1110    
	Rendez			vous; 
	Chan			*chan;			/* chan of open */ 
	int				dma;			/* dma chan, alloc on open, free on close */ 
	int				bufinit;		/* boolean, if buffers allocated */ 
	Buf				buf[Nbuf];		/* buffers and queues */ 
	volatile Buf	*current;		/* next dma to finish */ 
	volatile Buf	*next;			/* next candidate for dma */ 
	volatile Buf	*filling;		/* buffer being filled */ 
2000/1109    
}; 
2000/1103    
 
2000/1104    
static	struct 
{ 
	QLock; 
2000/1109    
	int		amode;			/* Aclosed/Aread/Awrite for /audio */ 
	int		intr;			/* boolean an interrupt has happened */ 
	int		rivol[Nvol];	/* right/left input/output volumes */ 
	int		livol[Nvol]; 
	int		rovol[Nvol]; 
	int		lovol[Nvol]; 
	ulong	totcount;		/* how many bytes processed since open */ 
	vlong	tottime;		/* time at which totcount bytes were processed */ 
	IOstate	i; 
	IOstate	o; 
2000/1103    
} audio; 
 
2000/1116    
static struct 
{ 
	ulong	bytes; 
	ulong	totaldma; 
	ulong	idledma; 
	ulong	faildma; 
} iostats; 
 
2000/1103    
static	struct 
{ 
	char*	name; 
	int	flag; 
	int	ilval;		/* initial values */ 
	int	irval; 
} volumes[] = 
{ 
[Vaudio]	"audio",	Fout, 		50,	50, 
[Vsynth]	"synth",	Fin|Fout,	0,	0, 
[Vcd]		"cd",		Fin|Fout,	0,	0, 
[Vline]		"line",		Fin|Fout,	0,	0, 
[Vmic]		"mic",		Fin|Fout|Fmono,	0,	0, 
[Vspeaker]	"speaker",	Fout|Fmono,	0,	0, 
 
[Vtreb]		"treb",		Fout, 		50,	50, 
[Vbass]		"bass",		Fout, 		50,	50, 
 
[Vspeed]	"speed",	Fin|Fout|Fmono,	Speed,	Speed, 
		0 
}; 
 
/* 
 * Grab control of the IIC/L3 shared pins 
 */ 
2000/1110    
static void 
L3_acquirepins(void) 
2000/1103    
{ 
2000/1110    
	gpioregs->set = (GPIO_L3_SCLK_o | GPIO_L3_SDA_io); 
	gpioregs->direction |=  (GPIO_L3_SCLK_o | GPIO_L3_SDA_io); 
2000/1103    
} 
 
/* 
 * Release control of the IIC/L3 shared pins 
 */ 
2000/1110    
static void 
L3_releasepins(void) 
2000/1103    
{ 
2000/1110    
	gpioregs->direction &= ~(GPIO_L3_SCLK_o | GPIO_L3_SDA_io); 
	gpioregs->clear = (GPIO_L3_SCLK_o | GPIO_L3_SDA_io); 
2000/1103    
} 
 
/* 
 * Initialize the interface 
 */ 
2000/1110    
static void  
L3_init(void) 
2000/1103    
{ 
2000/1110    
	gpioregs->altfunc &= ~(GPIO_L3_SDA_io | GPIO_L3_SCLK_o | GPIO_L3_MODE_o); 
	gpioregs->set = GPIO_L3_MODE_o; 
	gpioregs->direction |= GPIO_L3_MODE_o; 
2000/1103    
	L3_releasepins(); 
} 
 
/* 
 * Get a bit. The clock is high on entry and on exit. Data is read after 
 * the clock low time has expired. 
 */ 
2000/1110    
static int 
L3_getbit(void) 
2000/1103    
{ 
	int data; 
 
2000/1110    
	gpioregs->clear = GPIO_L3_SCLK_o; 
	µdelay(L3_ClockLowTime); 
2000/1103    
 
2000/1110    
	data = (gpioregs->level & GPIO_L3_SDA_io) ? 1 : 0; 
2000/1103    
 
2000/1110    
 	gpioregs->set = GPIO_L3_SCLK_o; 
	µdelay(L3_ClockHighTime); 
2000/1103    
 
	return data; 
} 
 
/* 
 * Send a bit. The clock is high on entry and on exit. Data is sent only 
 * when the clock is low (I2C compatibility). 
 */ 
2000/1110    
static void 
L3_sendbit(int bit) 
2000/1103    
{ 
2000/1110    
	gpioregs->clear = GPIO_L3_SCLK_o; 
2000/1103    
 
	if (bit & 1) 
2000/1110    
		gpioregs->set = GPIO_L3_SDA_io; 
2000/1103    
	else 
2000/1110    
		gpioregs->clear = GPIO_L3_SDA_io; 
2000/1103    
 
	/* Assumes L3_DataSetupTime < L3_ClockLowTime */ 
2000/1110    
	µdelay(L3_ClockLowTime); 
2000/1103    
 
2000/1110    
	gpioregs->set = GPIO_L3_SCLK_o; 
	µdelay(L3_ClockHighTime); 
2000/1103    
} 
 
/* 
 * Send a byte. The mode line is set or pulsed based on the mode sequence 
 * count. The mode line is high on entry and exit. The mod line is pulsed 
 * before the second data byte and before ech byte thereafter. 
 */ 
2000/1110    
static void 
L3_sendbyte(char data, int mode) 
2000/1103    
{ 
	int i; 
 
	L3_acquirepins(); 
 
	switch(mode) { 
2000/1110    
	case 0: /* Address mode */ 
		gpioregs->clear = GPIO_L3_MODE_o; 
2000/1103    
		break; 
2000/1110    
	case 1: /* First data byte */ 
2000/1103    
		break; 
2000/1110    
	default: /* Subsequent bytes */ 
		gpioregs->clear = GPIO_L3_MODE_o; 
		µdelay(L3_HaltTime); 
		gpioregs->set = GPIO_L3_MODE_o; 
2000/1103    
		break; 
	} 
 
2000/1110    
	µdelay(L3_ModeSetupTime); 
2000/1103    
 
	for (i = 0; i < 8; i++) 
		L3_sendbit(data >> i); 
 
	if (mode == 0)  /* Address mode */ 
2000/1110    
		gpioregs->set = GPIO_L3_MODE_o; 
2000/1103    
 
2000/1110    
	µdelay(L3_ModeHoldTime); 
2000/1103    
 
	L3_releasepins(); 
} 
 
/* 
 * Get a byte. The mode line is set or pulsed based on the mode sequence 
 * count. The mode line is high on entry and exit. The mod line is pulsed 
 * before the second data byte and before each byte thereafter. This 
 * function is never valid with mode == 0 (address cycle) as the address 
 * is always sent on the bus, not read. 
 */ 
2000/1110    
static char 
L3_getbyte(int mode) 
2000/1103    
{ 
	char data = 0; 
	int i; 
 
	L3_acquirepins(); 
2000/1110    
	gpioregs->direction &= ~(GPIO_L3_SDA_io); 
2000/1103    
 
	switch(mode) { 
2000/1110    
	case 0: /* Address mode - never valid */ 
2000/1103    
		break; 
2000/1110    
	case 1: /* First data byte */ 
2000/1103    
		break; 
2000/1110    
	default: /* Subsequent bytes */ 
		gpioregs->clear = GPIO_L3_MODE_o; 
		µdelay(L3_HaltTime); 
		gpioregs->set = GPIO_L3_MODE_o; 
2000/1103    
		break; 
	} 
 
2000/1110    
	µdelay(L3_ModeSetupTime); 
2000/1103    
 
	for (i = 0; i < 8; i++) 
		data |= (L3_getbit() << i); 
 
2000/1110    
	µdelay(L3_ModeHoldTime); 
2000/1103    
 
	L3_releasepins(); 
 
	return data; 
} 
 
/* 
 * Write data to a device on the L3 bus. The address is passed as well as 
 * the data and length. The length written is returned. The register space 
 * is encoded in the address (low two bits are set and device address is 
 * in the upper 6 bits). 
 */ 
2000/1110    
static int 
2000/1111    
L3_write(uchar addr, uchar *data, int len) 
2000/1103    
{ 
	int mode = 0; 
	int bytes = len; 
 
	L3_sendbyte(addr, mode++); 
	while(len--) 
		L3_sendbyte(*data++, mode++); 
 
	return bytes; 
} 
 
/* 
 * Read data from a device on the L3 bus. The address is passed as well as 
 * the data and length. The length read is returned. The register space 
 * is encoded in the address (low two bits are set and device address is 
 * in the upper 6 bits). 
 */ 
2000/1110    
static int 
2000/1111    
L3_read(uchar addr, uchar *data, int len) 
2000/1103    
{ 
	int mode = 0; 
	int bytes = len; 
 
	L3_sendbyte(addr, mode++); 
	while(len--) 
		*data++ = L3_getbyte(mode++); 
 
	return bytes; 
} 
 
static	char	Emode[]		= "illegal open mode"; 
static	char	Evolume[]	= "illegal volume specifier"; 
 
2000/1109    
static void 
bufinit(IOstate *b) 
2000/1103    
{ 
2000/1109    
	int i; 
2000/1103    
 
2000/1113    
	if (debug) print("#A: bufinit\n"); 
2000/1109    
	for (i = 0; i < Nbuf; i++) 
		b->buf[i].virt = xalloc(Bufsize); 
	b->bufinit = 1; 
}; 
2000/1103    
 
2000/1109    
static void 
setempty(IOstate *b) 
2000/1103    
{ 
2000/1109    
	int i; 
2000/1103    
 
2000/1113    
	if (debug) print("#A: setempty\n"); 
2000/1109    
	for (i = 0; i < Nbuf; i++) { 
		b->buf[i].nbytes = 0; 
	} 
	b->filling = b->buf; 
	b->current = b->buf; 
2000/1110    
	b->next = b->buf; 
2000/1103    
} 
 
2000/1110    
static int 
2000/1111    
audioqnotfull(void *x) 
2000/1110    
{ 
2000/1111    
	IOstate *s = x; 
 
2000/1110    
	/* called with lock */ 
2000/1116    
	return dmaidle(s->dma) || s->filling != s->current; 
2000/1110    
} 
 
2000/1103    
static void 
audioinit(void) 
{ 
2000/1110    
	/* do nothing */ 
} 
 
static void 
audioenable(void) 
{ 
2000/1111    
	uchar	data[2]; 
2000/1103    
 
	L3_init(); 
 
2000/1110    
	/* Setup the uarts */ 
2000/1116    
    ppcregs->assignment &= ~(1<<18); 
2000/1103    
 
2000/1111    
    gpioregs->altfunc &= ~(GPIO_SSP_TXD_o | GPIO_SSP_RXD_i | GPIO_SSP_SCLK_o | GPIO_SSP_SFRM_o); 
	gpioregs->altfunc |= (GPIO_SSP_CLK_i); 
	gpioregs->direction &= ~(GPIO_SSP_CLK_i); 
2000/1103    
 
2000/1116    
	sspregs->control0 = 0; 
	sspregs->control0 = 0x031f; /* 16 bits, TI frames, serial clock rate 3 */ 
	sspregs->control1 = 0x0020; /* ext clock */ 
	sspregs->control0 = 0x039f;	/* enable */ 
2000/1110    
 
	/* Enable the audio power */ 
2000/1111    
	audiopower(1); 
	amplifierpower(1); 
	audiomute(0); 
2000/1110    
 
2000/1116    
	/* external clock configured for 44100 samples/sec */ 
2000/1111    
	gpioregs->direction |=  (GPIO_CLK_SET0_o|GPIO_CLK_SET1_o); 
2000/1116    
/* This is purportedly the wrong way round: 0 should be set, 1 cleared */ 
    gpioregs->set	= GPIO_CLK_SET0_o|GPIO_CLK_SET1_o; 
//    gpioregs->clear	= GPIO_CLK_SET1_o; 
 
2000/1110    
	/* Wait for the UDA1341 to wake up */ 
2000/1111    
	delay(100); 
2000/1110    
 
2000/1111    
	/* Reset the chip */ 
2000/1116    
	data[0] = 2<<UdaStatusSC | 1<<UdaStatusIF | 1<<UdaStatusRST; 
2000/1111    
	L3_write(UDA1341_L3Addr<<2 | UDA1341_STATUS, data, 1 ); 
2000/1116    
	gpioregs->set = EGPIO_codec_reset; 
	gpioregs->clear = EGPIO_codec_reset; 
	/* write uda 1341 status[0] */ 
	data[0] &= ~(1<<UdaStatusRST); /* clear reset */ 
	L3_write(UDA1341_L3Addr<<2 | UDA1341_STATUS, data, 1 ); 
2000/1110    
 
2000/1111    
	/* write uda 1341 status[1] */ 
	data[0] = 0x80 | 0x3<<UdaStatusPC | 1<<UdaStatusIGS | 1<<UdaStatusOGS; 
	L3_write(UDA1341_L3Addr<<2 | UDA1341_STATUS, data, 1); 
 
	/* write uda 1341 data0[0] (volume) */ 
2000/1116    
	data[0] = 15 & 0x3f;	/* 6 bits, others must be 0 */ 
2000/1111    
	L3_write(UDA1341_L3Addr<<2 | UDA1341_DATA0, data, 1); 
 
	/* write uda 1341 data0[2] (mode switch) */ 
	data[0] = 0x80 | 3;	/* mode = 3 */ 
	L3_write(UDA1341_L3Addr<<2 | UDA1341_DATA0, data, 1); 
 
	/* set mixer mode and level */ 
	data[0] = 0xc0 | 2 /* ext address */; 
	data[1] = 0xe0 | 2 | 4 << 2;	/* mixer mode and mic level */ 
	L3_write(UDA1341_L3Addr<<2 | UDA1341_DATA0, data, 2); 
 
	/* set agc control and input amplifier gain */ 
	data[0] = 0xc0 | 4 /* ext address */; 
	data[1] = 0xe0 | 1<<4 | 3;	/* AGC control and input ampl gain */ 
	L3_write(UDA1341_L3Addr<<2 | UDA1341_DATA0, data, 2 ); 
 
	/* set agc time constant and output level */ 
	data[0] = 0xc0 | 6 /* ext address */; 
	data[1] = 0xe0 | 0<<2 | 3;	/* agc time constant and output level */ 
	L3_write(UDA1341_L3Addr<<2 | UDA1341_DATA0, data, 2 ); 
 
2000/1116    
	if (debug) { 
		print("#A: audio enabled\n"); 
		print("\tsspregs->control0 = 0x%lux\n", sspregs->control0); 
		print("\tsspregs->control1 = 0x%lux\n", sspregs->control1); 
	} 
2000/1103    
} 
2000/1107    
 
static void 
2000/1111    
sendaudio(IOstate *b) { 
2000/1115    
	/* interrupt routine calls this too */ 
2000/1116    
	int n; 
 
2000/1115    
	if (debug > 1) print("#A: sendaudio\n"); 
	ilock(&b->ilock); 
2000/1110    
	while (b->next != b->filling) { 
		assert(b->next->nbytes); 
2000/1116    
		if ((n = dmastart(b->dma, b->next->virt, b->next->nbytes)) == 0) { 
			iostats.faildma++; 
2000/1115    
			break; 
		} 
2000/1116    
		iostats.totaldma++; 
		if (n == 1) iostats.idledma++; 
2000/1115    
		if (debug > 1) print("#A: dmastart @%p\n", b->next); 
		b->next->nbytes = 0; 
2000/1110    
		b->next++; 
		if (b->next == &b->buf[Nbuf]) 
2000/1111    
			b->next = &b->buf[0]; 
2000/1110    
	} 
2000/1115    
	iunlock(&b->ilock); 
2000/1110    
} 
 
static void 
2000/1116    
audiointr(void *x, ulong ndma) { 
2000/1110    
	IOstate *s = x; 
 
	if (s == &audio.o) { 
		/* Only interrupt routine touches s->current */ 
2000/1115    
		if (debug > 1) iprint("#A: audio interrupt @%p\n", s->current); 
2000/1110    
		s->current++; 
2000/1111    
		if (s->current == &s->buf[Nbuf]) 
			s->current = &s->buf[0]; 
2000/1116    
		if (ndma > 0) 
			sendaudio(s); 
2000/1109    
	} 
2000/1111    
	wakeup(&s->vous); 
2000/1109    
} 
2000/1107    
 
static Chan* 
audioattach(char *param) 
{ 
	return devattach('A', param); 
} 
 
static int 
audiowalk(Chan *c, char *name) 
{ 
	return devwalk(c, name, audiodir, nelem(audiodir), devgen); 
} 
 
static void 
audiostat(Chan *c, char *db) 
{ 
	devstat(c, db, audiodir, nelem(audiodir), devgen); 
} 
 
static Chan* 
audioopen(Chan *c, int omode) 
{ 
 
	switch(c->qid.path & ~CHDIR) { 
	default: 
		error(Eperm); 
		break; 
 
	case Qstatus: 
		if((omode&7) != OREAD) 
			error(Eperm); 
	case Qvolume: 
	case Qdir: 
		break; 
 
	case Qaudio: 
2000/1109    
		omode = (omode & 0x7) + 1; 
2000/1110    
//		if (omode & ~(Aread | Awrite)) 
		if (omode & ~(Awrite)) 
2000/1108    
			error(Ebadarg); 
2000/1107    
		qlock(&audio); 
2000/1108    
		if(audio.amode & omode){ 
2000/1107    
			qunlock(&audio); 
			error(Einuse); 
		} 
2000/1111    
		audioenable(); 
2000/1116    
		memset(&iostats, 0, sizeof(iostats)); 
2000/1109    
		if (omode & Aread) { 
2000/1108    
			/* read */ 
2000/1109    
			audio.amode |= Aread; 
			if(audio.i.bufinit == 0) 
				bufinit(&audio.i); 
			setempty(&audio.i); 
2000/1114    
			audio.i.chan = c; 
			audio.i.dma = dmaalloc(0, 0, 8, 2, SSPRecvDMA, Port4SSP, audiointr, (void*)&audio.o); 
2000/1108    
		} 
		if (omode & 0x2) { 
			/* write */ 
2000/1111    
//			amplifierpower(1); 
//			audiomute(0); 
2000/1109    
			audio.amode |= Awrite; 
			if(audio.o.bufinit == 0) 
				bufinit(&audio.o); 
			setempty(&audio.o); 
2000/1110    
			audio.o.chan = c; 
2000/1114    
			audio.o.dma = dmaalloc(0, 0, 8, 2, SSPXmitDMA, Port4SSP, audiointr, (void*)&audio.o); 
2000/1107    
		} 
2000/1109    
//		mxvolume(); 
2000/1115    
		qunlock(&audio); 
2000/1113    
		if (debug) print("#A: open done\n"); 
2000/1107    
		break; 
	} 
	c = devopen(c, omode, audiodir, nelem(audiodir), devgen); 
	c->mode = openmode(omode); 
	c->flag |= COPEN; 
	c->offset = 0; 
 
	return c; 
} 
 
static void 
audioclose(Chan *c) 
{ 
 
	switch(c->qid.path & ~CHDIR) { 
	default: 
		error(Eperm); 
		break; 
 
	case Qdir: 
	case Qvolume: 
	case Qstatus: 
		break; 
 
	case Qaudio: 
2000/1115    
		if (debug > 1) print("#A: close\n"); 
2000/1107    
		if(c->flag & COPEN) { 
			qlock(&audio); 
2000/1110    
			qlock(&audio.o); 
2000/1107    
			if(waserror()){ 
2000/1110    
				qunlock(&audio.o); 
2000/1107    
				qunlock(&audio); 
				nexterror(); 
			} 
2000/1110    
			if (audio.o.chan == c) { 
				/* closing the write end */ 
				audio.amode &= ~Awrite; 
 
2000/1115    
				if (audio.o.filling->nbytes) { 
2000/1110    
					audio.o.filling++; 
					if (audio.o.filling == &audio.o.buf[Nbuf]) 
						audio.o.filling = &audio.o.buf[0]; 
					sendaudio(&audio.o); 
				} 
2000/1116    
				delay(2000); 
		//		dmawait(audio.o.dma); 
				if (!dmaidle(audio.o.dma)) 
					print("dma still busy\n"); 
2000/1111    
				amplifierpower(0); 
2000/1110    
				setempty(&audio.o); 
2000/1116    
				dmafree(audio.o.dma); 
2000/1110    
			} 
			if (audio.i.chan == c) { 
				/* closing the read end */ 
				audio.amode &= ~Aread; 
				setempty(&audio.i); 
			} 
			if (audio.amode == 0) { 
				/* turn audio off */ 
				audiopower(0); 
			} 
2000/1107    
			poperror(); 
2000/1110    
			qunlock(&audio.o); 
2000/1107    
			qunlock(&audio); 
2000/1116    
			print("total dmas: %lud\n", iostats.totaldma); 
			print("dmas while idle: %lud\n", iostats.idledma); 
			print("dmas while busy: %lud\n", iostats.faildma); 
2000/1107    
		} 
		break; 
	} 
} 
 
static long 
audioread(Chan *c, void *v, long n, vlong off) 
{ 
	int liv, riv, lov, rov; 
	long m, n0; 
	char buf[300]; 
	int j; 
	ulong offset = off; 
	char *a; 
 
	n0 = n; 
	a = v; 
	switch(c->qid.path & ~CHDIR) { 
	default: 
		error(Eperm); 
		break; 
 
	case Qdir: 
		return devdirread(c, a, n, audiodir, nelem(audiodir), devgen); 
 
	case Qaudio: 
		break; 
 
	case Qstatus: 
		buf[0] = 0; 
		snprint(buf, sizeof(buf), "bytes %lud\ntime %lld\n", 
			audio.totcount, audio.tottime); 
		return readstr(offset, a, n, buf); 
 
	case Qvolume: 
		j = 0; 
		buf[0] = 0; 
		for(m=0; volumes[m].name; m++){ 
			liv = audio.livol[m]; 
			riv = audio.rivol[m]; 
			lov = audio.lovol[m]; 
			rov = audio.rovol[m]; 
			j += snprint(buf+j, sizeof(buf)-j, "%s", volumes[m].name); 
			if((volumes[m].flag & Fmono) || liv==riv && lov==rov){ 
				if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov) 
					j += snprint(buf+j, sizeof(buf)-j, " %d", liv); 
				else{ 
					if(volumes[m].flag & Fin) 
						j += snprint(buf+j, sizeof(buf)-j, 
							" in %d", liv); 
					if(volumes[m].flag & Fout) 
						j += snprint(buf+j, sizeof(buf)-j, 
							" out %d", lov); 
				} 
			}else{ 
				if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && 
				    liv==lov && riv==rov) 
					j += snprint(buf+j, sizeof(buf)-j, 
						" left %d right %d", 
						liv, riv); 
				else{ 
					if(volumes[m].flag & Fin) 
						j += snprint(buf+j, sizeof(buf)-j, 
							" in left %d right %d", 
							liv, riv); 
					if(volumes[m].flag & Fout) 
						j += snprint(buf+j, sizeof(buf)-j, 
							" out left %d right %d", 
							lov, rov); 
				} 
			} 
			j += snprint(buf+j, sizeof(buf)-j, "\n"); 
		} 
		return readstr(offset, a, n, buf); 
	} 
	return n0-n; 
} 
 
static long 
audiowrite(Chan *c, void *vp, long n, vlong) 
{ 
	long m, n0; 
	int i, nf, v, left, right, in, out; 
	char buf[255], *field[Ncmd]; 
2000/1109    
	char *p; 
	IOstate *a; 
2000/1107    
 
2000/1109    
	p = vp; 
2000/1107    
	n0 = n; 
	switch(c->qid.path & ~CHDIR) { 
	default: 
		error(Eperm); 
		break; 
 
	case Qvolume: 
		v = Vaudio; 
		left = 1; 
		right = 1; 
		in = 1; 
		out = 1; 
		if(n > sizeof(buf)-1) 
			n = sizeof(buf)-1; 
2000/1109    
		memmove(buf, p, n); 
2000/1107    
		buf[n] = '\0'; 
 
		nf = getfields(buf, field, Ncmd, 1, " \t\n"); 
		for(i = 0; i < nf; i++){ 
			/* 
			 * a number is volume 
			 */ 
			if(field[i][0] >= '0' && field[i][0] <= '9') { 
				m = strtoul(field[i], 0, 10); 
				if(left && out) 
					audio.lovol[v] = m; 
				if(left && in) 
					audio.livol[v] = m; 
				if(right && out) 
					audio.rovol[v] = m; 
				if(right && in) 
					audio.rivol[v] = m; 
				goto cont0; 
			} 
 
			for(m=0; volumes[m].name; m++) { 
				if(strcmp(field[i], volumes[m].name) == 0) { 
					v = m; 
					in = 1; 
					out = 1; 
					left = 1; 
					right = 1; 
					goto cont0; 
				} 
			} 
 
			if(strcmp(field[i], "reset") == 0) { 
2000/1111    
//				resetlevel(); 
//				mxvolume(); 
2000/1107    
				goto cont0; 
			} 
			if(strcmp(field[i], "in") == 0) { 
				in = 1; 
				out = 0; 
				goto cont0; 
			} 
			if(strcmp(field[i], "out") == 0) { 
				in = 0; 
				out = 1; 
				goto cont0; 
			} 
			if(strcmp(field[i], "left") == 0) { 
				left = 1; 
				right = 0; 
				goto cont0; 
			} 
			if(strcmp(field[i], "right") == 0) { 
				left = 0; 
				right = 1; 
				goto cont0; 
			} 
			error(Evolume); 
			break; 
		cont0:; 
		} 
		break; 
 
	case Qaudio: 
2000/1115    
		if (debug > 1) print("#A: write %ld\n", n); 
2000/1109    
		if((audio.amode & Awrite) == 0) 
2000/1107    
			error(Emode); 
2000/1109    
		a = &audio.o; 
		qlock(a); 
2000/1107    
		if(waserror()){ 
2000/1109    
			qunlock(a); 
2000/1107    
			nexterror(); 
		} 
		while(n > 0) { 
2000/1109    
			/* wait if dma in progress */ 
2000/1116    
			while (!dmaidle(a->dma) && a->filling == a->current) { 
2000/1115    
				if (debug > 1) print("#A: sleep\n"); 
2000/1110    
				sleep(&a->vous, audioqnotfull, a); 
2000/1115    
			} 
2000/1107    
 
2000/1109    
			m = Bufsize - a->filling->nbytes; 
2000/1107    
			if(m > n) 
				m = n; 
2000/1111    
			memmove(a->filling->virt + a->filling->nbytes, p, m); 
2000/1107    
 
2000/1109    
			a->filling->nbytes += m; 
2000/1107    
			n -= m; 
2000/1109    
			p += m; 
			if(a->filling->nbytes >= Bufsize) { 
2000/1115    
				if (debug > 1) print("#A: filled @%p\n", a->filling); 
2000/1109    
				a->filling++; 
				if (a->filling == &a->buf[Nbuf]) 
					a->filling = a->buf; 
2000/1110    
				sendaudio(a); 
2000/1107    
			} 
		} 
		poperror(); 
2000/1109    
		qunlock(a); 
2000/1107    
		break; 
	} 
	return n0 - n; 
} 
 
2000/1111    
Dev uda1341devtab = { 
2000/1107    
	'A', 
	"audio", 
 
	devreset, 
	audioinit, 
	audioattach, 
	devclone, 
	audiowalk, 
	audiostat, 
	audioopen, 
	devcreate, 
	audioclose, 
	audioread, 
	devbread, 
	audiowrite, 
	devbwrite, 
	devremove, 
	devwstat, 
}; 


source code copyright © 1990-2005 Lucent Technologies; see license
Plan 9 distribution
comments to russ cox (rsc@swtch.com)