Subject: kern/28459: autri driver hangs for 30 seconds on close
To: None <kern-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: Andreas Gustafsson <gson@gson.org>
List: netbsd-bugs
Date: 11/29/2004 14:00:01
>Number:         28459
>Category:       kern
>Synopsis:       autri driver hangs for 30 seconds on close
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Mon Nov 29 14:00:00 +0000 2004
>Originator:     Andreas Gustafsson
>Release:        NetBSD-current as of Nov 28, 2004
>Organization:
>Environment:
System: NetBSD guam.araneus.fi 2.0B NetBSD 2.0B (GUAM) #0: Sun Nov 28 11:58:04 EET 2004 root@guava.araneus.fi:/usr/src/sys/arch/i386/compile/GUAM i386
Architecture: i386
Machine: i386
>Description:

When using a full-duplex audio application with the autri audio
driver, a close() of the audio device will hang for 30 seconds in
audio_drain() before finally returning.

This was seen on a Toshiba Libretto L2, which has an ALi M5451
sound chip.

>How-To-Repeat:

On a system with a sound chip that uses the autri driver, extract the
enclosed shell archive, type "make fullduplex" and then "sh test.sh".
It will play about a tenth of a second of silence while simultaneously
recording to /dev/null, and then attempt to exit.  Notice how the
process hangs for 30 seconds after printing the message "copied 5120
bytes".

# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	test.sh
#	fullduplex.cc
#
echo x - test.sh
sed 's/^X//' >test.sh << 'END-of-test.sh'
Xaudioctl -n -f /dev/sound -w \
X  record.rate=48000 record.channels=1 record.precision=16 \
X  record.encoding=slinear_le \
X  play.rate=48000 play.channels=1 play.precision=16 \
X  play.encoding=slinear_le 
X
Xdd if=/dev/zero bs=512 count=10 | ./fullduplex /dev/sound >/dev/null
END-of-test.sh
echo x - fullduplex.cc
sed 's/^X//' >fullduplex.cc << 'END-of-fullduplex.cc'
X// $Id: fdtunnel.cc,v 1.2 2002/08/18 02:41:28 gson Exp $
X//
X// Full duplex audio I/O for use in pipelines.
X//
X// (C) Copyright 1999-2004 Araneus Information Systems Oy.
X//
X// Simultaneously plays audio data from stdin and records onto stdout.
X//
X
X#include <stdio.h>
X
X#include <stdio.h>
X
X#include <sys/types.h>
X#include <sys/audioio.h>
X#include <sys/ioctl.h>
X#include <sys/mman.h>
X#include <sys/uio.h>
X#include <sys/param.h>
X#include <sys/file.h>
X
X#include <stdlib.h>
X#include <syslog.h>
X#include <string.h>
X#include <errno.h>
X#include <unistd.h>
X
X#define BSIZ 256
X
Xchar *progname;
X
X// Log an error message and exit
X
Xvoid error(char *s) {
X    fprintf(stderr, "%s: %s: %s\n", progname, s, strerror(errno));
X    exit(1);
X}
X
X// This structure represents a single (unidirectional) copy 
X// operation in progress, like a single instance of the "cat"
X// command.
X  
Xtypedef struct {
X    char buf[BSIZ];
X    int r;	/* read file descriptor */
X    int w;	/* write file descriptor */
X    char *p;	/* current position in buffer */
X    int n;	/* amount of unwritten data in buffer */
X    int eof;	/* 0 if open; 1 if eof received; 2 if eof sent */
X    int copied;
X} cat;
X
X// Do N simultaneous "cat" operations; return when they have all
X// seen EOF.
X
Xvoid copy(int catc, cat *catv)
X{
X    fd_set readfds, writefds, exceptfds;
X    int i, n;
X    
X    for (i = 0; i < catc; i++) {
X	cat *c = &catv[i];
X	c->n = 0;
X	c->eof = 0;
X	c->copied = 0;
X    }
X    
X    for (;;) {
X	int max;
X
X	FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds);
X	max = -1;
X	
X	for (i = 0; i < catc; i++) {
X	    cat *c = &catv[i];
X	    if (c->n || c->eof == 1) {
X		FD_SET(c->w, &writefds);
X		if (c->w > max)
X		    max = c->w;
X	    } else if (!c->eof) {
X		FD_SET(c->r, &readfds);
X		if (c->r > max)
X		    max = c->r;
X	    }
X	}
X
X	if (catv[0].eof && catv[1].copied >= catv[0].copied)
X	    break;
X	    
X	n = select(max+1, &readfds, &writefds, &exceptfds,
X		   (struct timeval *) 0);
X	if (n == -1)
X	    error("select");
X
X	for (i = 0; i < catc; i++) {
X	    cat *c = &catv[i];
X	    if (FD_ISSET(c->r, &readfds)) {
X		n = read(c->r, c->buf, BSIZ);
X		if (n == 0) {
X		    fprintf(stderr, "eof reading fd %d\n", c->r);
X		    c->eof = 1;
X		} else if (n == -1) {
X		    fprintf(stderr, "error reading cat %d\n", i);
X		    error("read");
X		} else {
X		    c->n = n;
X		    c->p = c->buf;
X		}
X	    }
X	    if (FD_ISSET(c->w, &writefds)) {
X		if (c->n) {
X		    n = write(c->w, c->p, c->n);
X		    if (n == -1)
X			error("write");
X		    c->n -= n;
X		    c->p += n;
X		    c->copied += n;
X		}
X	    }	    
X	}
X    }
X}
X    
Xint main(int argc, char **argv) {
X    progname = argv[0];
X
X    int fd = open(argv[1], O_RDWR);
X    if (fd < 0) {
X	fprintf(stderr, "%s: open: %s\n",
X		argv[1], strerror(errno));
X	exit(1);
X    }
X
X    // Check audio device properties
X    int props;
X    int r = ioctl(fd, AUDIO_GETPROPS, &props);
X    if ((props & AUDIO_PROP_FULLDUPLEX) != AUDIO_PROP_FULLDUPLEX) {
X	fprintf(stderr, "Audio device does not support "
X		"full duplex.  Sorry.\n");
X	exit(1);
X    }
X    
X    // Set full duplex mode
X    int yes = 1;
X    r = ioctl(fd, AUDIO_SETFD, &yes);
X    if (r < 0) {
X	fprintf(stderr, "ioctl AUDIO_SETFD: %s\n",
X		strerror(errno));
X	exit(1);
X    }
X    
X    // Set an appropriate block size
X    audio_info_t info;	
X    AUDIO_INITINFO(&info);
X    info.blocksize = 4800;
X    // Due to a bug in the audio driver, the block size may not actually get
X    // set unless we set info.mode = AUMODE_RECORD | AUMODE_PLAY redundantly.
X    info.mode = AUMODE_RECORD | AUMODE_PLAY;
X    r = ioctl(fd, AUDIO_SETINFO, &info);
X    if (r < 0) {
X	fprintf(stderr, "ioctl AUDIO_SETINFO blocksize/mode: %s\n",
X		strerror(errno));
X	exit(1);
X    }
X
X    AUDIO_INITINFO(&info);
X    info.record.pause = 0;
X    r = ioctl(fd, AUDIO_SETINFO, &info);
X    if (r < 0) {
X	fprintf(stderr, "ioctl AUDIO_SETINFO unpause: %s\n",
X		strerror(errno));
X	exit(1);
X    }
X    
X    cat cats[2];
X    cats[0].r = 0;
X    cats[0].w = fd;
X    cats[1].r = fd;
X    cats[1].w = 1;
X    copy(2, cats);
X    fprintf(stderr, "copied %d bytes\n", cats[1].copied);
X    close(fd);
X    return 0;
X}
END-of-fullduplex.cc
exit

>Fix:

Not known.