git: 9front

Download patch

ref: e6819e877e1d9aec3a374fdce1fa1f463fc19ec3
parent: 396e564996f3666b1ad4c10b4ad21dade40607e9
author: cinap_lenrek <cinap_lenrek@gmx.de>
date: Fri Jan 18 20:12:39 EST 2013

audiohda: first attempt on audio recording support for intel hda audio, distinguish mode in audio code

--- a/sys/src/9/pc/audioac97.c
+++ b/sys/src/9/pc/audioac97.c
@@ -306,7 +306,7 @@
 inavail(void *arg)
 {
 	Ring *r = arg;
-	return buffered(r);
+	return buffered(r) > 0;
 }
 
 static int
@@ -313,7 +313,7 @@
 outavail(void *arg)
 {
 	Ring *r = arg;
-	return available(r);
+	return available(r) > 0;
 }
 
 static int
@@ -386,11 +386,14 @@
 }
 
 static void
-ac97close(Audio *adev)
+ac97close(Audio *adev, int mode)
 {
 	Ctlr *ctlr;
 	Ring *ring;
 	uchar z[1];
+
+	if(mode == OREAD)
+		return;
 
 	z[0] = 0;
 	ctlr = adev->ctlr;
--- a/sys/src/9/pc/audiohda.c
+++ b/sys/src/9/pc/audiohda.c
@@ -11,6 +11,8 @@
 typedef struct Ctlr Ctlr;
 typedef struct Bld Bld;
 typedef struct Ring Ring;
+typedef struct Stream Stream;
+
 typedef struct Id Id;
 typedef struct Widget Widget;
 typedef struct Codec Codec;
@@ -226,6 +228,26 @@
 	ulong	wi;
 };
 
+struct Stream {
+	Ring	ring;
+
+	Bld	*blds;
+
+	uint	sdctl;
+	uint	sdintr;
+	uint	sdnum;
+
+	uint	afmt;
+	uint	atag;
+	int	active;
+
+	uint	pin;
+	uint	cad;
+
+	Widget	*conv;	/* DAC or ADC */
+	Widget	*jack;	/* the pin jack */
+};
+
 struct Id {
 	Ctlr *ctlr;
 	uint codec, nid;
@@ -245,8 +267,10 @@
 			uint convrate, convfmt;
 		};
 	};
-	Widget *next;
-	Widget *from;
+	Widget *next;	/* next in function group */
+	Widget *path;	/* next in audio path */
+
+	Widget *link;	/* temporary for findpath */
 };
 
 struct Fungroup {
@@ -254,8 +278,6 @@
 	Codec *codec;
 	uint type;
 	Widget *first;
-	Widget *mixer;
-	Widget *src, *dst;
 	Fungroup *next;
 };
 
@@ -294,25 +316,13 @@
 	ulong *rirb;
 	ulong rirbsize;
 	
-	uint sdctl;
-	uint sdintr;
-	uint sdnum;
+	Stream sout;
+	Stream sin;
 
-	Bld *blds;
-
-	Ring ring;
-
 	uint iss, oss, bss;
 
 	uint codecmask;	
 	Codec *codec[Maxcodecs];
-
-	Widget *amp, *src;
-	uint pin;
-	uint cad;
-
-	int active;
-	uint afmt, atag;
 };
 
 #define csr32(c, r)	(*(ulong *)&(c)->mem[r])
@@ -589,55 +599,84 @@
 }
 
 static Widget *
-findpath(Widget *src)
+findpath(Widget *jack, int type)
 {
 	Widget *q[Maxwidgets];
 	uint l, r, i;
-	Widget *w, *v;
-	
+	Widget *w, *to;
+
 	l = r = 0;
-	q[r++] = src;
-	for(w=src->fg->first; w; w=w->next)
-		w->from = nil;
-	src->from = src;
+	for(w=jack->fg->first; w != nil; w = w->next)
+		w->link = nil;
 
+	if(type == Waout){
+		q[r++] = jack;
+		jack->link = jack;
+	} else {
+		for(w=jack->fg->first; w != nil; w = w->next)
+			if(w->type == type){
+				q[r++] = w;
+				w->link = w;
+			}
+	}
+
 	while(l < r){
 		w = q[l++];
-		if(w->type == Waout)
+		if(type == Waout){
+			if(w->type == type)
+				return w;
+		} else if(w == jack){
+			for(w = jack->link; w != nil; w = w->link)
+				if(w->type == type)
+					return w;
 			break;
+		}
 		for(i=0; i<w->nlist; i++){
-			v = w->list[i];
-			if(v == nil || v->from)
+			to = w->list[i];
+			if(to == nil || to->link)
 				continue;
-			v->from = w;
-			q[r++] = v;
+			to->link = w;
+			q[r++] = to;
 		}
 	}
-	if(w->type != Waout)
-		return nil;
-	return w;
+
+	return nil;
 }
 
 static void
-connectpath(Widget *src, Widget *dst)
+disconnectpath(Widget *from, Widget *to)
 {
-	Widget *w, *v;
+	Widget *next;
+
+	for(; from != nil && from != to; from = next){
+		next = from->path;
+		from->path = nil;
+		setoutamp(from, 1, nil);
+		setinamp(next, from, 1, nil);
+	}
+	setoutamp(to, 1, nil);
+}
+
+static void
+connectpath(Widget *from, Widget *to)
+{
+	Widget *next;
 	uint i;
 
-	for(w=dst; w != src; w=v){
-		v = w->from;
-		setoutamp(w, 0, nil);
-		setinamp(v, w, 0, nil);
-		if(v->nlist == 1)
+	for(; from != nil && from != to; from = next){
+		next = from->link;
+		from->path = next;
+		setoutamp(from, 0, nil);
+		setinamp(next, from, 0, nil);
+		if(next->nlist == 1)
 			continue;
-		for(i=0; i < v->nlist; i++)
-			if(v->list[i] == w){
-				cmd(v->id, Setconn, i);	
+		for(i=0; i < next->nlist; i++)
+			if(next->list[i] == from){
+				cmd(next->id, Setconn, i);	
 				break;
 			}
 	}
-	setoutamp(src, 0, nil);
-	cmd(src->id, Setpinctl, Pinctlout);
+	setoutamp(to, 0, nil);
 }
 
 static void
@@ -844,41 +883,48 @@
 }
 
 static int
-connectpin(Ctlr *ctlr, uint pin, uint cad)
+connectpin(Ctlr *ctlr, Stream *s, int type, uint pin, uint cad)
 {
-	Widget *w, *src, *dst;
+	Widget *jack, *conv;
 
 	if(cad >= Maxcodecs || pin >= Maxwidgets || ctlr->codec[cad] == nil)
 		return -1;
-	src = ctlr->codec[cad]->widgets[pin];
-	if(src == nil)
+	jack = ctlr->codec[cad]->widgets[pin];
+	if(jack == nil)
 		return -1;
-	if(src->type != Wpin)
+	if(jack->type != Wpin)
 		return -1;
-	if((src->pincap & Pout) == 0)
-		return -1;
 
-	dst = findpath(src);
-	if(dst == nil)
+	conv = findpath(jack, type);
+	if(conv == nil)
 		return -1;
 
-	/* mute all widgets, clear stream */
-	for(w=src->fg->first; w != nil; w=w->next){
-		setoutamp(w, 1, nil);
-		setinamp(w, nil, 1, nil);
-		cmd(w->id, Setstream, 0);
+	if(s->conv != nil && s->jack != nil){
+		if(s->conv->type == Waout)
+			disconnectpath(s->conv, s->jack);
+		else
+			disconnectpath(s->jack, s->conv);
+		cmd(s->conv->id, Setstream, 0);
+		cmd(s->jack->id, Setpinctl, 0);
 	}
 
-	connectpath(src, dst);
+	if(type == Waout){
+		connectpath(conv, jack);
+		cmd(jack->id, Setpinctl, Pinctlout);
+	} else {
+		connectpath(jack, conv);
+		cmd(jack->id, Setpinctl, Pinctlin);
+	}
 
-	cmd(dst->id, Setconvfmt, ctlr->afmt);
-	cmd(dst->id, Setstream, (ctlr->atag << 4) | 0);
-	cmd(dst->id, Setchancnt, 1);
+	cmd(conv->id, Setconvfmt, s->afmt);
+	cmd(conv->id, Setstream, (s->atag << 4) | 0);
+	cmd(conv->id, Setchancnt, 1);
 
-	ctlr->amp = dst;
-	ctlr->src = src;
-	ctlr->pin = pin;
-	ctlr->cad = cad;
+	s->conv = conv;
+	s->jack = jack;
+	s->pin = pin;
+	s->cad = cad;
+
 	return 0;
 }
 
@@ -950,6 +996,29 @@
 }
 
 static long
+readring(Ring *r, uchar *p, long n)
+{
+	long n0, m;
+
+	n0 = n;
+	while(n > 0){
+		if((m = buffered(r)) <= 0)
+			break;
+		if(m > n)
+			m = n;
+		if(p){
+			if(r->ri + m > r->nbuf)
+				m = r->nbuf - r->ri;
+			memmove(p, r->buf + r->ri, m);
+			p += m;
+		}
+		r->ri = (r->ri + m) % r->nbuf;
+		n -= m;
+	}
+	return n0 - n;
+}
+
+static long
 writering(Ring *r, uchar *p, long n)
 {
 	long n0, m;
@@ -973,93 +1042,87 @@
 }
 
 static int
-streamalloc(Ctlr *ctlr)
+streamalloc(Ctlr *ctlr, Stream *s, int num)
 {
 	Ring *r;
 	int i;
 
-	r = &ctlr->ring;
-	memset(r, 0, sizeof(*r));
+	r = &s->ring;
 	r->buf = xspanalloc(r->nbuf = Bufsize, 128, 0);
-	ctlr->blds = xspanalloc(Nblocks * sizeof(Bld), 128, 0);
-	if(r->buf == nil || ctlr->blds == nil){
+	s->blds = xspanalloc(Nblocks * sizeof(Bld), 128, 0);
+	if(r->buf == nil || s->blds == nil){
 		print("hda: no memory for stream\n");
 		return -1;
 	}
 	for(i=0; i<Nblocks; i++){
-		ctlr->blds[i].addrlo = PADDR(r->buf) + i*Blocksize;
-		ctlr->blds[i].addrhi = 0;
-		ctlr->blds[i].len = Blocksize;
-		ctlr->blds[i].flags = 0x01;	/* interrupt on completion */
+		s->blds[i].addrlo = PADDR(r->buf) + i*Blocksize;
+		s->blds[i].addrhi = 0;
+		s->blds[i].len = Blocksize;
+		s->blds[i].flags = 0x01;	/* interrupt on completion */
 	}
 
-	/* output dma engine starts after inputs */
-	ctlr->sdnum = ctlr->iss;
-	ctlr->sdctl = Sdctl0 + ctlr->sdnum*0x20;
-	ctlr->sdintr = 1<<ctlr->sdnum;
-	ctlr->atag = ctlr->sdnum+1;
-	ctlr->afmt = Fmtstereo | Fmtsampw | Fmtdiv1 | Fmtmul1 | Fmtbase441;
-	ctlr->active = 0;
+	s->sdnum = num;
+	s->sdctl = Sdctl0 + s->sdnum*0x20;
+	s->sdintr = 1<<s->sdnum;
+	s->atag = s->sdnum+1;
+	s->afmt = Fmtstereo | Fmtsampw | Fmtdiv1 | Fmtmul1 | Fmtbase441;
+	s->active = 0;
 
 	/* perform reset */
-	csr8(ctlr, ctlr->sdctl) &= ~(Srst | Srun | Scie | Seie | Sdie);
-	csr8(ctlr, ctlr->sdctl) |= Srst;
+	csr8(ctlr, s->sdctl) &= ~(Srst | Srun | Scie | Seie | Sdie);
+	csr8(ctlr, s->sdctl) |= Srst;
 	microdelay(Codecdelay);
-	waitup8(ctlr, ctlr->sdctl, Srst, Srst);
-	csr8(ctlr, ctlr->sdctl) &= ~Srst;
+	waitup8(ctlr, s->sdctl, Srst, Srst);
+	csr8(ctlr, s->sdctl) &= ~Srst;
 	microdelay(Codecdelay);
-	waitup8(ctlr, ctlr->sdctl, Srst, 0);
+	waitup8(ctlr, s->sdctl, Srst, 0);
 
 	/* set stream number */
-	csr32(ctlr, ctlr->sdctl) = (ctlr->atag << Stagbit) |
-		(csr32(ctlr, ctlr->sdctl) & ~(0xF << Stagbit));
+	csr32(ctlr, s->sdctl) = (s->atag << Stagbit) |
+		(csr32(ctlr, s->sdctl) & ~(0xF << Stagbit));
 
 	/* set stream format */
-	csr16(ctlr, Sdfmt+ctlr->sdctl) = ctlr->afmt;
+	csr16(ctlr, Sdfmt+s->sdctl) = s->afmt;
 
 	/* program stream DMA & parms */
-	csr32(ctlr, Sdbdplo+ctlr->sdctl) = PADDR(ctlr->blds);
-	csr32(ctlr, Sdbdphi+ctlr->sdctl) = 0;
-	csr32(ctlr, Sdcbl+ctlr->sdctl) = r->nbuf;
-	csr16(ctlr, Sdlvi+ctlr->sdctl) = (Nblocks - 1) & 0xff;
+	csr32(ctlr, Sdbdplo+s->sdctl) = PADDR(s->blds);
+	csr32(ctlr, Sdbdphi+s->sdctl) = 0;
+	csr32(ctlr, Sdcbl+s->sdctl) = r->nbuf;
+	csr16(ctlr, Sdlvi+s->sdctl) = (Nblocks - 1) & 0xff;
 
 	/* mask out ints */
-	csr8(ctlr, Sdsts+ctlr->sdctl) = Scompl | Sfifoerr | Sdescerr;
+	csr8(ctlr, Sdsts+s->sdctl) = Scompl | Sfifoerr | Sdescerr;
 
 	/* enable global intrs for this stream */
-	csr32(ctlr, Intctl) |= ctlr->sdintr;
-	csr8(ctlr, ctlr->sdctl) |= Scie | Seie | Sdie;
+	csr32(ctlr, Intctl) |= s->sdintr;
+	csr8(ctlr, s->sdctl) |= Scie | Seie | Sdie;
 
 	return 0;
 }
 
 static void
-streamstart(Ctlr *ctlr)
+streamstart(Ctlr *ctlr, Stream *s)
 {
-	ctlr->active = 1;
-	
-	csr8(ctlr, ctlr->sdctl) |= Srun;
-	waitup8(ctlr, ctlr->sdctl, Srun, Srun);
+	s->active = 1;
+	csr8(ctlr, s->sdctl) |= Srun;
+	waitup8(ctlr, s->sdctl, Srun, Srun);
 }
 
 static void
-streamstop(Ctlr *ctlr)
+streamstop(Ctlr *ctlr, Stream *s)
 {
-	csr8(ctlr, ctlr->sdctl) &= ~Srun;
-	waitup8(ctlr, ctlr->sdctl, Srun, 0);
-
-	ctlr->active = 0;
+	csr8(ctlr, s->sdctl) &= ~Srun;
+	waitup8(ctlr, s->sdctl, Srun, 0);
+	s->active = 0;
 }
 
 static uint
-streampos(Ctlr *ctlr)
+streampos(Ctlr *ctlr, Stream *s)
 {
-	Ring *r;
 	uint p;
 
-	r = &ctlr->ring;
-	p = csr32(ctlr, Sdlpib+ctlr->sdctl);
-	if(p >= r->nbuf)
+	p = csr32(ctlr, Sdlpib+s->sdctl);
+	if(p >= s->ring.nbuf)
 		p = 0;
 	return p;
 }
@@ -1085,12 +1148,21 @@
 		if(ntok <= 0)
 			continue;
 		if(cistrcmp(tok[0], "pin") == 0 && ntok >= 2){
-			cad = ctlr->cad;
+			cad = ctlr->sout.cad;
 			pin = strtoul(tok[1], 0, 0);
 			if(ntok > 2)
 				cad = strtoul(tok[2], 0, 0);
-			connectpin(ctlr, pin, cad);
+			if(connectpin(ctlr, &ctlr->sout, Waout, pin, cad) < 0)
+				error("connectpin failed");
 		}else
+		if(cistrcmp(tok[0], "inpin") == 0 && ntok >= 2){
+			cad = ctlr->sout.cad;
+			pin = strtoul(tok[1], 0, 0);
+			if(ntok > 2)
+				cad = strtoul(tok[2], 0, 0);
+			if(connectpin(ctlr, &ctlr->sin, Wain, pin, cad) < 0)
+				error("connectpin failed");
+		}else
 			error(Ebadctl);
 	}
 	return n;
@@ -1097,10 +1169,17 @@
 }
 
 static int
+inavail(void *arg)
+{
+	Ring *r = arg;
+	return buffered(r) > 0;
+}
+
+static int
 outavail(void *arg)
 {
-	Ctlr *ctlr = arg;
-	return available(&ctlr->ring) > 0;
+	Ring *r = arg;
+	return available(r) > 0;
 }
 
 static int
@@ -1108,7 +1187,7 @@
 {
 	Ctlr *ctlr = arg;
 	int delay = ctlr->adev->delay*BytesPerSample;
-	return (delay <= 0) || (buffered(&ctlr->ring) <= delay) || (ctlr->active == 0);
+	return (delay <= 0) || (buffered(&ctlr->sout.ring) <= delay) || (ctlr->sout.active == 0);
 }
 
 static long
@@ -1116,19 +1195,44 @@
 {
 	Ctlr *ctlr;
 	ctlr = adev->ctlr;
-	return buffered(&ctlr->ring);
+	return buffered(&ctlr->sout.ring);
 }
 
 static void
 hdakick(Ctlr *ctlr)
 {
-	if(ctlr->active)
+	if(ctlr->sout.active)
 		return;
-	if(buffered(&ctlr->ring) > Blocksize)
-		streamstart(ctlr);
+	if(buffered(&ctlr->sout.ring) > Blocksize)
+		streamstart(ctlr, &ctlr->sout);
 }
 
 static long
+hdaread(Audio *adev, void *vp, long n, vlong)
+{
+	uchar *p, *e;
+	Ctlr *ctlr;
+	Ring *ring;
+
+	p = vp;
+	e = p + n;
+	ctlr = adev->ctlr;
+	ring = &ctlr->sin.ring;
+	if(ring->buf == nil || ctlr->sin.conv == nil)
+		return 0;
+	while(p < e) {
+		if((n = readring(ring, p, e - p)) <= 0){
+			if(!ctlr->sin.active)
+				streamstart(ctlr, &ctlr->sin);
+			sleep(&ring->r, inavail, ring);
+			continue;
+		}
+		p += n;
+	}
+	return p - (uchar*)vp;
+}
+
+static long
 hdawrite(Audio *adev, void *vp, long n, vlong)
 {
 	uchar *p, *e;
@@ -1138,11 +1242,11 @@
 	p = vp;
 	e = p + n;
 	ctlr = adev->ctlr;
-	ring = &ctlr->ring;
+	ring = &ctlr->sout.ring;
 	while(p < e) {
 		if((n = writering(ring, p, e - p)) <= 0){
 			hdakick(ctlr);
-			sleep(&ring->r, outavail, ctlr);
+			sleep(&ring->r, outavail, ring);
 			continue;
 		}
 		p += n;
@@ -1153,23 +1257,28 @@
 }
 
 static void
-hdaclose(Audio *adev)
+hdaclose(Audio *adev, int mode)
 {
 	Ctlr *ctlr;
 	uchar z[1];
-	Ring *r;
 
 	ctlr = adev->ctlr;
-	if(!ctlr->active)
-		return;
-	z[0] = 0;
-	r = &ctlr->ring;
-	while(r->wi % Blocksize)
-		hdawrite(adev, z, sizeof(z), 0);
+	if(mode == OREAD || mode == ORDWR){
+		if(ctlr->sin.active)
+			streamstop(ctlr, &ctlr->sin);
+	}
+	if(mode == OWRITE || mode == ORDWR){
+		if(ctlr->sout.active){
+			z[0] = 0;
+			while(ctlr->sout.ring.wi % Blocksize)
+				hdawrite(adev, z, sizeof(z), 0);
+		}
+	}
 }
 
 enum {
 	Vmaster,
+	Vrecord,
 	Vspeed,
 	Vdelay,
 	Nvol,
@@ -1177,6 +1286,7 @@
 
 static Volume voltab[] = {
 	[Vmaster] "master", 0, 0x7f, Stereo, 0,
+	[Vrecord] "record", 0, 0x7f, Stereo, 0,
 	[Vspeed] "speed", 0, 0, Absolute, 0,
 	[Vdelay] "delay", 0, 0, Absolute, 0,
 	0
@@ -1189,8 +1299,8 @@
 
 	switch(x){
 	case Vmaster:
-		if(ctlr->amp != nil)
-			getoutamp(ctlr->amp, a);
+		if(ctlr->sout.conv != nil)
+			getoutamp(ctlr->sout.conv, a);
 		break;
 	case Vspeed:
 		a[0] = adev->speed;
@@ -1209,9 +1319,13 @@
 
 	switch(x){
 	case Vmaster:
-		if(ctlr->amp != nil)
-			setoutamp(ctlr->amp, 0, a);
+		if(ctlr->sout.conv != nil)
+			setoutamp(ctlr->sout.conv, 0, a);
 		break;
+	case Vrecord:
+		if(ctlr->sin.conv != nil)
+			setinamp(ctlr->sin.conv, nil, 0, a);
+		break;
 	case Vspeed:
 		adev->speed = a[0];
 		break;
@@ -1226,8 +1340,8 @@
 fillvoltab(Ctlr *ctlr, Volume *vt)
 {
 	memmove(vt, voltab, sizeof(voltab));
-	if(ctlr->amp != nil)
-		vt[Vmaster].range = getoutamprange(ctlr->amp);
+	if(ctlr->sout.conv != nil)
+		vt[Vmaster].range = getoutamprange(ctlr->sout.conv);
 }
 
 static long
@@ -1251,25 +1365,35 @@
 {
 	Ctlr *ctlr;
 	Audio *adev;
-	uint sts;
 	Ring *r;
+	uint sts;
 
 	adev = arg;
 	ctlr = adev->ctlr;
-	
 	ilock(ctlr);
 	sts = csr32(ctlr, Intsts);
-	if(sts & ctlr->sdintr){
-		/* ack interrupt */
-		csr8(ctlr, Sdsts+ctlr->sdctl) |= Scompl;
-		r = &ctlr->ring;
-		r->ri = streampos(ctlr);
-		if(ctlr->active && buffered(r) < Blocksize){
-			streamstop(ctlr);
-			r->ri = r->wi = streampos(ctlr);
+	if(sts & ctlr->sout.sdintr){
+		csr8(ctlr, Sdsts+ctlr->sout.sdctl) |= Scompl;
+
+		r = &ctlr->sout.ring;
+		r->ri = streampos(ctlr, &ctlr->sout);
+		if(ctlr->sout.active && buffered(r) < Blocksize){
+			streamstop(ctlr, &ctlr->sout);
+			r->ri = r->wi = streampos(ctlr, &ctlr->sout);
 		}
 		wakeup(&r->r);
 	}
+	if(sts & ctlr->sin.sdintr){
+		csr8(ctlr, Sdsts+ctlr->sin.sdctl) |= Scompl;
+
+		r = &ctlr->sin.ring;
+		r->wi = streampos(ctlr, &ctlr->sin);
+		if(ctlr->sin.active && available(r) < Blocksize){
+			streamstop(ctlr, &ctlr->sin);
+			r->ri = r->wi = streampos(ctlr, &ctlr->sin);
+		}
+		wakeup(&r->r);
+	}
 	iunlock(ctlr);
 }
 
@@ -1286,20 +1410,21 @@
 	
 	s = a;
 	e = s + n;
-	s = seprint(s, e, "bufsize %6d buffered %6ld\n", Blocksize, buffered(&ctlr->ring));
+	s = seprint(s, e, "bufsize %6d buffered %6ld\n", Blocksize, buffered(&ctlr->sout.ring));
 	for(i=0; i<Maxcodecs; i++){
 		if((codec = ctlr->codec[i]) == nil)
 			continue;
-		s = seprint(s, e, "codec %2d pin %3d\n",
-			codec->id.codec, ctlr->pin);
+		s = seprint(s, e, "codec %2d pin %3d inpin %3d\n",
+			codec->id.codec, ctlr->sout.pin, ctlr->sin.pin);
 		for(fg=codec->fgroup; fg; fg=fg->next){
 			for(w=fg->first; w; w=w->next){
 				if(w->type != Wpin)
 					continue;
 				r = w->pin;
-				s = seprint(s, e, "pin %3d %s %s %s %s %s %s%s%s\n",
+				s = seprint(s, e, "pin %3d %s%s %s %s %s %s %s%s%s\n",
 					w->id.nid,
-					(w->pincap & Pout) != 0 ? "out" : "in",
+					(w->pincap & Pin) != 0 ? "in" : "",
+					(w->pincap & Pout) != 0 ? "out" : "",
 					pinport[(r >> 30) & 0x3],
 					pinloc2[(r >> 28) & 0x3],
 					pinloc[(r >> 24) & 0xf],
@@ -1312,16 +1437,26 @@
 		}
 	}
 
-	s = seprint(s, e, "path ");
-	for(w=ctlr->amp; w != nil; w = w->from){
+	s = seprint(s, e, "outpath ");
+	for(w=ctlr->sout.conv; w != nil; w = w->path){
 		s = seprint(s, e, "%s %3d %lux %lux %lux", widtype[w->type&7], w->id.nid,
 			(ulong)w->cap, (ulong)w->pin, (ulong)w->pincap);
-		if(w == ctlr->src)
+		if(w == ctlr->sout.jack)
 			break;
 		s = seprint(s, e, " → ");
 	}
 	s = seprint(s, e, "\n");
 
+	s = seprint(s, e, "inpath ");
+	for(w=ctlr->sin.jack; w != nil; w = w->path){
+		s = seprint(s, e, "%s %3d %lux %lux %lux", widtype[w->type&7], w->id.nid,
+			(ulong)w->cap, (ulong)w->pin, (ulong)w->pincap);
+		if(w == ctlr->sin.conv)
+			break;
+		s = seprint(s, e, " → ");
+	}
+	s = seprint(s, e, "\n");
+
 	return s - (char*)a;
 }
 
@@ -1561,10 +1696,13 @@
 		print("#A%d: unable to start hda\n", ctlr->no);
 		return -1;
 	}
-	if(streamalloc(ctlr) < 0){
-		print("#A%d: streamalloc failed\n", ctlr->no);
+	if(streamalloc(ctlr, &ctlr->sout, ctlr->iss) < 0){
+		print("#A%d: output streamalloc failed\n", ctlr->no);
 		return -1;
 	}
+	if(ctlr->iss > 0)
+		if(streamalloc(ctlr, &ctlr->sin, 0) < 0)
+			print("#A%d: input streamalloc failed\n", ctlr->no);
 	if(enumdev(ctlr) < 0){
 		print("#A%d: no audio codecs found\n", ctlr->no);
 		return -1;
@@ -1574,11 +1712,12 @@
 		print("#A%d: no output pins found!\n", ctlr->no);
 		return -1;
 	}
-	if(connectpin(ctlr, best, cad) < 0){
+	if(connectpin(ctlr, &ctlr->sout, Waout, best, cad) < 0){
 		print("#A%d: error connecting pin\n", ctlr->no);
 		return -1;
 	}
 
+	adev->read = hdaread;
 	adev->write = hdawrite;
 	adev->close = hdaclose;
 	adev->buffered = hdabuffered;
--- a/sys/src/9/pc/audiosb16.c
+++ b/sys/src/9/pc/audiosb16.c
@@ -554,10 +554,12 @@
 }
 
 static void
-audioclose(Audio *adev)
+audioclose(Audio *adev, int mode)
 {
 	Ctlr *ctlr;
 
+	if(mode == OREAD)
+		return;
 	ctlr = adev->ctlr;
 	sleep(&ctlr->vous, inactive, ctlr);
 	setempty(ctlr);
--- a/sys/src/9/port/audioif.h
+++ b/sys/src/9/port/audioif.h
@@ -13,7 +13,7 @@
 
 	long (*read)(Audio *, void *, long, vlong);
 	long (*write)(Audio *, void *, long, vlong);
-	void (*close)(Audio *);
+	void (*close)(Audio *, int);
 
 	long (*volread)(Audio *, void *, long, vlong);
 	long (*volwrite)(Audio *, void *, long, vlong);
--- a/sys/src/9/port/devaudio.c
+++ b/sys/src/9/port/devaudio.c
@@ -311,7 +311,7 @@
 	if((c->qid.path == Qaudio) && (c->flag & COPEN)){
 		if(adev->close){
 			if(!waserror()){
-				adev->close(adev);
+				adev->close(adev, c->mode);
 				poperror();
 			}
 		}
--