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