sound.c revision 54155
164562Sgshapiro/*
2261363Sgshapiro * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
364562Sgshapiro * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it)
464562Sgshapiro * All rights reserved.
564562Sgshapiro *
664562Sgshapiro * Redistribution and use in source and binary forms, with or without
764562Sgshapiro * modification, are permitted provided that the following conditions
864562Sgshapiro * are met:
964562Sgshapiro * 1. Redistributions of source code must retain the above copyright
1064562Sgshapiro *    notice, this list of conditions and the following disclaimer.
1164562Sgshapiro * 2. Redistributions in binary form must reproduce the above copyright
1264562Sgshapiro *    notice, this list of conditions and the following disclaimer in the
1364562Sgshapiro *    documentation and/or other materials provided with the distribution.
1464562Sgshapiro *
1590792Sgshapiro * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1690792Sgshapiro * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1764562Sgshapiro * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18266692Sgshapiro * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1964562Sgshapiro * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2090792Sgshapiro * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2190792Sgshapiro * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2264562Sgshapiro * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2364562Sgshapiro * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2464562Sgshapiro * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2564562Sgshapiro * SUCH DAMAGE.
2664562Sgshapiro *
2764562Sgshapiro * $FreeBSD: head/sys/dev/sound/pcm/sound.c 54155 1999-12-05 19:09:13Z cg $
2864562Sgshapiro */
2964562Sgshapiro
3064562Sgshapiro#include "opt_devfs.h"
3164562Sgshapiro
3264562Sgshapiro#include <dev/sound/pcm/sound.h>
3364562Sgshapiro#ifdef DEVFS
3464562Sgshapiro#include <sys/devfsext.h>
3564562Sgshapiro#endif /* DEVFS */
3664562Sgshapiro
3764562Sgshapiro#if NPCM > 0	/* from "pcm.h" via disgusting #include in snd/sound.h */
3864562Sgshapiro
3964562Sgshapiroextern struct isa_driver pcmdriver;
4064562Sgshapiro
4164562Sgshapirostatic int 	status_isopen = 0;
4264562Sgshapirostatic int 	status_init(char *buf, int size);
4364562Sgshapirostatic int 	status_read(struct uio *buf);
4464562Sgshapiro
4564562Sgshapirostatic d_open_t sndopen;
4664562Sgshapirostatic d_close_t sndclose;
4764562Sgshapirostatic d_ioctl_t sndioctl;
4864562Sgshapirostatic d_read_t sndread;
4964562Sgshapirostatic d_write_t sndwrite;
5064562Sgshapirostatic d_mmap_t sndmmap;
5164562Sgshapirostatic d_poll_t sndpoll;
5264562Sgshapiro
5364562Sgshapiro#define CDEV_MAJOR 30
5464562Sgshapirostatic struct cdevsw snd_cdevsw = {
5564562Sgshapiro	/* open */	sndopen,
5664562Sgshapiro	/* close */	sndclose,
5764562Sgshapiro	/* read */	sndread,
5864562Sgshapiro	/* write */	sndwrite,
5964562Sgshapiro	/* ioctl */	sndioctl,
6064562Sgshapiro	/* poll */	sndpoll,
6198121Sgshapiro	/* mmap */	sndmmap,
6264562Sgshapiro	/* strategy */	nostrategy,
6364562Sgshapiro	/* name */	"snd",
6490792Sgshapiro	/* maj */	CDEV_MAJOR,
6564562Sgshapiro	/* dump */	nodump,
6664562Sgshapiro	/* psize */	nopsize,
6790792Sgshapiro	/* flags */	0,
6864562Sgshapiro	/* bmaj */	-1
6964562Sgshapiro};
7090792Sgshapiro
7164562Sgshapiro/* PROPOSAL:
7264562Sgshapiroeach unit needs:
7364562Sgshapirostatus, mixer, dsp, dspW, audio, sequencer, midi-in, seq2, sndproc = 9 devices
7490792SgshapirodspW and audio are deprecated.
7590792Sgshapirodsp needs min 64 channels, will give it 256
7664562Sgshapiro
7764562Sgshapirominor = (unit << 12) + (dev << 8) + channel
7864562Sgshapirocurrently minor = (channel << 8) + (unit << 4) + dev
7964562Sgshapiro
8064562Sgshapironomenclature:
8164562Sgshapiro	/dev/pcmX/dsp.(0..255)
8264562Sgshapiro	/dev/pcmX/dspW
8364562Sgshapiro	/dev/pcmX/audio
8464562Sgshapiro	/dev/pcmX/status
8564562Sgshapiro	/dev/pcmX/mixer
8664562Sgshapiro	[etc.]
8764562Sgshapiro
8864562Sgshapirocurrently:
8964562Sgshapirominor = (channel << 8) + (unit << 4) + dev
9064562Sgshapiro*/
9164562Sgshapiro
9264562Sgshapiro#define PCMMINOR(x) (minor(x))
9364562Sgshapiro#define PCMCHAN(x) ((PCMMINOR(x) & 0x0000ff00) >> 8)
9464562Sgshapiro#define PCMUNIT(x) ((PCMMINOR(x) & 0x000000f0) >> 4)
9564562Sgshapiro#define PCMDEV(x)   (PCMMINOR(x) & 0x0000000f)
9690792Sgshapiro#define PCMMKMINOR(u, d) (((u) & 0x0f) << 4 | ((d) & 0x0f))
9764562Sgshapiro
9864562Sgshapirostatic devclass_t pcm_devclass;
9964562Sgshapiro
10064562Sgshapirostatic snddev_info *
10164562Sgshapirogsd(int unit)
10264562Sgshapiro{
10364562Sgshapiro	return devclass_get_softc(pcm_devclass, unit);
10464562Sgshapiro}
10564562Sgshapiro
10664562Sgshapiroint
10764562Sgshapiropcm_addchan(device_t dev, int dir, pcm_channel *templ, void *devinfo)
10864562Sgshapiro{
10964562Sgshapiro    	snddev_info *d = device_get_softc(dev);
11064562Sgshapiro	pcm_channel *ch;
11164562Sgshapiro
11264562Sgshapiro	ch = (dir == PCMDIR_PLAY)? &d->play[d->playcount++] : &d->rec[d->reccount++];
11364562Sgshapiro	*ch = *templ;
11464562Sgshapiro	chn_init(ch, devinfo, dir);
11564562Sgshapiro	d->chancount++;
11664562Sgshapiro	return 0;
11764562Sgshapiro}
11864562Sgshapiro
11964562Sgshapiroint
12064562Sgshapiropcm_setstatus(device_t dev, char *str)
12164562Sgshapiro{
12264562Sgshapiro    	snddev_info *d = device_get_softc(dev);
12364562Sgshapiro	strncpy(d->status, str, SND_STATUSLEN);
12464562Sgshapiro	return 0;
12564562Sgshapiro}
12664562Sgshapiro
12764562Sgshapirou_int32_t
12864562Sgshapiropcm_getflags(device_t dev)
12964562Sgshapiro{
13064562Sgshapiro    	snddev_info *d = device_get_softc(dev);
13164562Sgshapiro	return d->flags;
13264562Sgshapiro}
13364562Sgshapiro
13464562Sgshapirovoid
13564562Sgshapiropcm_setflags(device_t dev, u_int32_t val)
13664562Sgshapiro{
13764562Sgshapiro    	snddev_info *d = device_get_softc(dev);
13864562Sgshapiro	d->flags = val;
13964562Sgshapiro}
14064562Sgshapiro
14164562Sgshapiro/* This is the generic init routine */
14290792Sgshapiroint
14364562Sgshapiropcm_register(device_t dev, void *devinfo, int numplay, int numrec)
14464562Sgshapiro{
14564562Sgshapiro    	int sz, unit = device_get_unit(dev);
14664562Sgshapiro    	snddev_info *d = device_get_softc(dev);
14764562Sgshapiro
14864562Sgshapiro    	if (!pcm_devclass) {
14964562Sgshapiro    		pcm_devclass = device_get_devclass(dev);
15064562Sgshapiro		make_dev(&snd_cdevsw, PCMMKMINOR(0, SND_DEV_STATUS),
15164562Sgshapiro			 UID_ROOT, GID_WHEEL, 0444, "sndstat");
15290792Sgshapiro	}
15364562Sgshapiro	make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL),
15464562Sgshapiro		 UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit);
15564562Sgshapiro	make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP),
15664562Sgshapiro		 UID_ROOT, GID_WHEEL, 0666, "dsp%d", unit);
15764562Sgshapiro	make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_AUDIO),
15864562Sgshapiro		 UID_ROOT, GID_WHEEL, 0666, "audio%d", unit);
15964562Sgshapiro	make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP16),
16064562Sgshapiro		 UID_ROOT, GID_WHEEL, 0666, "dspW%d", unit);
16164562Sgshapiro	/* XXX SND_DEV_NORESET? */
16264562Sgshapiro	d->devinfo = devinfo;
16364562Sgshapiro	d->chancount = d->playcount = d->reccount = 0;
16464562Sgshapiro    	sz = (numplay + numrec) * sizeof(pcm_channel *);
16564562Sgshapiro    	d->aplay = (pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT);
16664562Sgshapiro    	if (!d->aplay) goto no;
16764562Sgshapiro    	d->arec = (pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT);
16864562Sgshapiro    	if (!d->arec) goto no;
16964562Sgshapiro    	bzero(d->aplay, sz);
17064562Sgshapiro    	bzero(d->arec, sz);
17164562Sgshapiro
17264562Sgshapiro    	d->play = (pcm_channel *)malloc(numplay * sizeof(pcm_channel),
17364562Sgshapiro					M_DEVBUF, M_NOWAIT);
17464562Sgshapiro    	if (!d->play) goto no;
17564562Sgshapiro    	d->rec = (pcm_channel *)malloc(numrec * sizeof(pcm_channel),
17664562Sgshapiro				       M_DEVBUF, M_NOWAIT);
17764562Sgshapiro    	if (!d->rec) goto no;
17864562Sgshapiro    	bzero(d->play, numplay * sizeof(pcm_channel));
17964562Sgshapiro    	bzero(d->rec, numrec * sizeof(pcm_channel));
18064562Sgshapiro
18164562Sgshapiro	fkchan_setup(&d->fakechan);
18264562Sgshapiro	chn_init(&d->fakechan, NULL, 0);
18364562Sgshapiro	d->magic = MAGIC(unit); /* debugging... */
18464562Sgshapiro
18564562Sgshapiro    	return 0;
18664562Sgshapirono:
18764562Sgshapiro	if (d->aplay) free(d->aplay, M_DEVBUF);
18864562Sgshapiro	if (d->play) free(d->play, M_DEVBUF);
18964562Sgshapiro	if (d->arec) free(d->arec, M_DEVBUF);
19064562Sgshapiro	if (d->rec) free(d->rec, M_DEVBUF);
19190792Sgshapiro	return ENXIO;
19264562Sgshapiro}
19364562Sgshapiro
19464562Sgshapiro/*
19564562Sgshapiro * a small utility function which, given a device number, returns
19664562Sgshapiro * a pointer to the associated snddev_info struct, and sets the unit
19764562Sgshapiro * number.
19864562Sgshapiro */
19964562Sgshapirostatic snddev_info *
20064562Sgshapiroget_snddev_info(dev_t i_dev, int *unit, int *dev, int *chan)
20164562Sgshapiro{
20264562Sgshapiro    	int u, d, c;
20364562Sgshapiro
20464562Sgshapiro    	u = PCMUNIT(i_dev);
20564562Sgshapiro    	d = PCMDEV(i_dev);
20664562Sgshapiro    	c = PCMCHAN(i_dev);
20764562Sgshapiro    	if (u > devclass_get_maxunit(pcm_devclass)) u = -1;
20864562Sgshapiro    	if (unit) *unit = u;
20990792Sgshapiro    	if (dev) *dev = d;
21064562Sgshapiro    	if (chan) *chan = c;
21164562Sgshapiro    	if (u < 0) return NULL;
21264562Sgshapiro
21364562Sgshapiro    	switch(d) {
21464562Sgshapiro    	case SND_DEV_CTL:	/* /dev/mixer handled by pcm */
21564562Sgshapiro    	case SND_DEV_STATUS: /* /dev/sndstat handled by pcm */
21664562Sgshapiro    	case SND_DEV_DSP:
21764562Sgshapiro    	case SND_DEV_DSP16:
21864562Sgshapiro    	case SND_DEV_AUDIO:
21964562Sgshapiro		return gsd(u);
22064562Sgshapiro
22164562Sgshapiro    	case SND_DEV_SEQ: /* XXX when enabled... */
22264562Sgshapiro    	case SND_DEV_SEQ2:
22364562Sgshapiro    	case SND_DEV_MIDIN:
22464562Sgshapiro    	case SND_DEV_SNDPROC:	/* /dev/sndproc handled by pcm */
22564562Sgshapiro    	default:
22664562Sgshapiro		printf("unsupported subdevice %d\n", d);
22764562Sgshapiro		return NULL;
22864562Sgshapiro    	}
22964562Sgshapiro}
23064562Sgshapiro
23164562Sgshapirostatic int
23264562Sgshapirosndopen(dev_t i_dev, int flags, int mode, struct proc *p)
23364562Sgshapiro{
23464562Sgshapiro    	int dev, unit, chan;
23564562Sgshapiro    	snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
23664562Sgshapiro
23764562Sgshapiro    	DEB(printf("open snd%d subdev %d flags 0x%08x mode 0x%08x\n",
23864562Sgshapiro		unit, dev, flags, mode));
23964562Sgshapiro
24064562Sgshapiro    	switch(dev) {
24190792Sgshapiro    	case SND_DEV_STATUS:
24264562Sgshapiro		if (status_isopen) return EBUSY;
24390792Sgshapiro		status_isopen = 1;
24490792Sgshapiro		return 0;
24564562Sgshapiro
24690792Sgshapiro    	case SND_DEV_CTL:
24790792Sgshapiro		return d? 0 : ENXIO;
24890792Sgshapiro
24990792Sgshapiro    	case SND_DEV_AUDIO:
25064562Sgshapiro    	case SND_DEV_DSP:
25164562Sgshapiro    	case SND_DEV_DSP16:
25264562Sgshapiro	case SND_DEV_NORESET:
25364562Sgshapiro		return d? dsp_open(d, chan, flags, dev) : ENXIO;
25464562Sgshapiro
25564562Sgshapiro    	default:
25664562Sgshapiro    		return ENXIO;
25764562Sgshapiro    	}
25864562Sgshapiro}
25964562Sgshapiro
26090792Sgshapirostatic int
26190792Sgshapirosndclose(dev_t i_dev, int flags, int mode, struct proc *p)
26264562Sgshapiro{
26364562Sgshapiro    	int dev, unit, chan;
26464562Sgshapiro    	snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
26564562Sgshapiro
26664562Sgshapiro    	DEB(printf("close snd%d subdev %d\n", unit, dev));
26764562Sgshapiro
26890792Sgshapiro    	switch(dev) { /* only those for which close makes sense */
26990792Sgshapiro    	case SND_DEV_STATUS:
27064562Sgshapiro		if (!status_isopen) return EBADF;
27164562Sgshapiro		status_isopen = 0;
27264562Sgshapiro		return 0;
27364562Sgshapiro
27464562Sgshapiro    	case SND_DEV_CTL:
27564562Sgshapiro		return d? 0 : ENXIO;
27690792Sgshapiro
27790792Sgshapiro    	case SND_DEV_AUDIO:
27864562Sgshapiro    	case SND_DEV_DSP:
27964562Sgshapiro    	case SND_DEV_DSP16:
28064562Sgshapiro		return d? dsp_close(d, chan, dev) : ENXIO;
28164562Sgshapiro
28264562Sgshapiro    	default:
28364562Sgshapiro		return ENXIO;
28490792Sgshapiro    	}
28590792Sgshapiro}
28664562Sgshapiro
28764562Sgshapirostatic int
28864562Sgshapirosndread(dev_t i_dev, struct uio *buf, int flag)
28964562Sgshapiro{
29064562Sgshapiro    	int dev, unit, chan;
29190792Sgshapiro    	snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
29290792Sgshapiro    	DEB(printf("read snd%d subdev %d flag 0x%08x\n", unit, dev, flag));
29364562Sgshapiro
29464562Sgshapiro    	switch(dev) {
29564562Sgshapiro    	case SND_DEV_STATUS:
29664562Sgshapiro		return status_isopen? status_read(buf) : EBADF;
29764562Sgshapiro
29890792Sgshapiro    	case SND_DEV_AUDIO:
29990792Sgshapiro    	case SND_DEV_DSP:
30064562Sgshapiro    	case SND_DEV_DSP16:
30164562Sgshapiro        	return d? dsp_read(d, chan, buf, flag) : EBADF;
30264562Sgshapiro
30364562Sgshapiro    	default:
30464562Sgshapiro    		return ENXIO;
30564562Sgshapiro    	}
30664562Sgshapiro}
307132943Sgshapiro
30890792Sgshapirostatic int
30964562Sgshapirosndwrite(dev_t i_dev, struct uio *buf, int flag)
31064562Sgshapiro{
31164562Sgshapiro    	int dev, unit, chan;
31264562Sgshapiro    	snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
31364562Sgshapiro
31490792Sgshapiro    	DEB(printf("write snd%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag));
31564562Sgshapiro
31664562Sgshapiro    	switch(dev) {	/* only writeable devices */
31764562Sgshapiro    	case SND_DEV_DSP:
31864562Sgshapiro    	case SND_DEV_DSP16:
31964562Sgshapiro    	case SND_DEV_AUDIO:
32064562Sgshapiro		return d? dsp_write(d, chan, buf, flag) : EBADF;
32164562Sgshapiro
32264562Sgshapiro    	default:
32364562Sgshapiro		return EPERM; /* for non-writeable devices ; */
32464562Sgshapiro    	}
32564562Sgshapiro}
32664562Sgshapiro
32764562Sgshapirostatic int
32864562Sgshapirosndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p)
32964562Sgshapiro{
33064562Sgshapiro    	int dev, chan;
33164562Sgshapiro    	snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan);
33264562Sgshapiro
33364562Sgshapiro    	if (d == NULL) return ENXIO;
33464562Sgshapiro
33564562Sgshapiro    	switch(dev) {
33664562Sgshapiro    	case SND_DEV_CTL:
33764562Sgshapiro		return mixer_ioctl(d, cmd, arg);
33864562Sgshapiro
33964562Sgshapiro    	case SND_DEV_AUDIO:
34064562Sgshapiro    	case SND_DEV_DSP:
34164562Sgshapiro    	case SND_DEV_DSP16:
34264562Sgshapiro		return dsp_ioctl(d, chan, cmd, arg);
34364562Sgshapiro
34464562Sgshapiro    	default:
34564562Sgshapiro    		return ENXIO;
34664562Sgshapiro    	}
34764562Sgshapiro}
34864562Sgshapiro
34964562Sgshapirostatic int
35064562Sgshapirosndpoll(dev_t i_dev, int events, struct proc *p)
35164562Sgshapiro{
35264562Sgshapiro    	int dev, chan;
35364562Sgshapiro    	snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan);
35490792Sgshapiro
35564562Sgshapiro	DEB(printf("sndpoll d 0x%p dev 0x%04x events 0x%08x\n", d, dev, events));
35690792Sgshapiro
35764562Sgshapiro    	if (d == NULL) return ENXIO;
35864562Sgshapiro
35964562Sgshapiro    	switch(dev) {
36064562Sgshapiro    	case SND_DEV_AUDIO:
36164562Sgshapiro    	case SND_DEV_DSP:
36264562Sgshapiro    	case SND_DEV_DSP16:
36390792Sgshapiro		return dsp_poll(d, chan, events, p);
36464562Sgshapiro
36564562Sgshapiro    	default:
36664562Sgshapiro    		return (events &
36790792Sgshapiro       		       (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)) | POLLHUP;
36864562Sgshapiro    	}
36964562Sgshapiro}
37090792Sgshapiro
37164562Sgshapiro/*
37264562Sgshapiro * The mmap interface allows access to the play and read buffer,
37364562Sgshapiro * plus the device descriptor.
37464562Sgshapiro * The various blocks are accessible at the following offsets:
37564562Sgshapiro *
37664562Sgshapiro * 0x00000000 ( 0   ) : write buffer ;
37764562Sgshapiro * 0x01000000 (16 MB) : read buffer ;
37864562Sgshapiro * 0x02000000 (32 MB) : device descriptor (dangerous!)
37964562Sgshapiro *
38064562Sgshapiro * WARNING: the mmap routines assume memory areas are aligned. This
38164562Sgshapiro * is true (probably) for the dma buffers, but likely false for the
38264562Sgshapiro * device descriptor. As a consequence, we do not know where it is
38364562Sgshapiro * located in the requested area.
38464562Sgshapiro */
38564562Sgshapirostatic int
38664562Sgshapirosndmmap(dev_t i_dev, vm_offset_t offset, int nprot)
38764562Sgshapiro{
38864562Sgshapiro    	int unit, dev, chan;
38964562Sgshapiro    	snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
39064562Sgshapiro
39164562Sgshapiro    	DEB(printf("sndmmap d 0x%p dev 0x%04x ofs 0x%08x nprot 0x%08x\n",
39264562Sgshapiro		   d, dev, offset, nprot));
39364562Sgshapiro
39464562Sgshapiro    	if (d == NULL || nprot & PROT_EXEC)	return -1; /* forbidden */
39564562Sgshapiro
39664562Sgshapiro    	switch(dev) {
39764562Sgshapiro    	case SND_DEV_AUDIO:
39864562Sgshapiro    	case SND_DEV_DSP:
39964562Sgshapiro    	case SND_DEV_DSP16:
40064562Sgshapiro		return dsp_mmap(d, chan, offset, nprot);
40164562Sgshapiro
40264562Sgshapiro    	default:
40364562Sgshapiro    		return -1;
40464562Sgshapiro    	}
40564562Sgshapiro}
40664562Sgshapiro
40764562Sgshapirostatic int
40864562Sgshapirostatus_init(char *buf, int size)
40998121Sgshapiro{
41064562Sgshapiro    	int             i;
41164562Sgshapiro    	device_t	    dev;
41264562Sgshapiro    	snddev_info     *d;
41364562Sgshapiro
41464562Sgshapiro    	snprintf(buf, size, "FreeBSD Audio Driver (newpcm) %s %s\n"
41564562Sgshapiro		 "Installed devices:\n", __DATE__, __TIME__);
41690792Sgshapiro
41790792Sgshapiro    	for (i = 0; i <= devclass_get_maxunit(pcm_devclass); i++) {
41890792Sgshapiro		d = gsd(i);
41964562Sgshapiro		if (!d) continue;
42064562Sgshapiro		dev = devclass_get_device(pcm_devclass, i);
42164562Sgshapiro        	if (1) snprintf(buf + strlen(buf), size - strlen(buf),
42264562Sgshapiro		            	"pcm%d: <%s> %s (%d/%d channels%s)\n",
42364562Sgshapiro		            	i, device_get_desc(dev), d->status,
42490792Sgshapiro		            	d->playcount, d->reccount,
42564562Sgshapiro			    	(!(d->flags & SD_F_SIMPLEX))? " duplex" : "");
42664562Sgshapiro    	}
42764562Sgshapiro    	return strlen(buf);
42864562Sgshapiro}
42964562Sgshapiro
43064562Sgshapirostatic int
43190792Sgshapirostatus_read(struct uio *buf)
43264562Sgshapiro{
43364562Sgshapiro    	static char	status_buf[4096];
43464562Sgshapiro    	static int 	bufptr = 0, buflen = 0;
43564562Sgshapiro    	int l;
43664562Sgshapiro
43764562Sgshapiro    	if (status_isopen == 1) {
43864562Sgshapiro		status_isopen++;
43964562Sgshapiro		bufptr = 0;
44064562Sgshapiro		buflen = status_init(status_buf, sizeof status_buf);
44164562Sgshapiro    	}
44264562Sgshapiro
44364562Sgshapiro    	l = min(buf->uio_resid, buflen - bufptr);
44464562Sgshapiro    	bufptr += l;
44564562Sgshapiro    	return (l > 0)? uiomove(status_buf + bufptr - l, l, buf) : 0;
44664562Sgshapiro}
44764562Sgshapiro
44864562Sgshapiro#endif	/* NPCM > 0 */
44964562Sgshapiro