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.