1987e5972SCameron Grant /* 2987e5972SCameron Grant * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> 3987e5972SCameron Grant * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it) 4987e5972SCameron Grant * All rights reserved. 5987e5972SCameron Grant * 6987e5972SCameron Grant * Redistribution and use in source and binary forms, with or without 7987e5972SCameron Grant * modification, are permitted provided that the following conditions 8987e5972SCameron Grant * are met: 9987e5972SCameron Grant * 1. Redistributions of source code must retain the above copyright 10987e5972SCameron Grant * notice, this list of conditions and the following disclaimer. 11987e5972SCameron Grant * 2. Redistributions in binary form must reproduce the above copyright 12987e5972SCameron Grant * notice, this list of conditions and the following disclaimer in the 13987e5972SCameron Grant * documentation and/or other materials provided with the distribution. 14987e5972SCameron Grant * 15987e5972SCameron Grant * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16987e5972SCameron Grant * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17987e5972SCameron Grant * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18987e5972SCameron Grant * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19987e5972SCameron Grant * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20987e5972SCameron Grant * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21987e5972SCameron Grant * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22987e5972SCameron Grant * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23987e5972SCameron Grant * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24987e5972SCameron Grant * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25987e5972SCameron Grant * SUCH DAMAGE. 26987e5972SCameron Grant * 2753c5a968SPeter Wemm * $FreeBSD$ 28987e5972SCameron Grant */ 29987e5972SCameron Grant 30ef9308b1SCameron Grant #include <dev/sound/pcm/sound.h> 31285648f9SCameron Grant #include <dev/sound/pcm/vchan.h> 327c438dbeSCameron Grant #include <sys/sysctl.h> 33285648f9SCameron Grant #include <sys/sbuf.h> 34285648f9SCameron Grant 35285648f9SCameron Grant #include "feeder_if.h" 36285648f9SCameron Grant 37285648f9SCameron Grant #undef SNDSTAT_VERBOSE 38987e5972SCameron Grant 3933dbf14aSCameron Grant static dev_t status_dev = 0; 40285648f9SCameron Grant static int do_status(int action, struct uio *buf); 41987e5972SCameron Grant 42987e5972SCameron Grant static d_open_t sndopen; 43987e5972SCameron Grant static d_close_t sndclose; 44987e5972SCameron Grant static d_ioctl_t sndioctl; 45987e5972SCameron Grant static d_read_t sndread; 46987e5972SCameron Grant static d_write_t sndwrite; 47987e5972SCameron Grant static d_mmap_t sndmmap; 48987e5972SCameron Grant static d_poll_t sndpoll; 49987e5972SCameron Grant 50987e5972SCameron Grant #define CDEV_MAJOR 30 51987e5972SCameron Grant static struct cdevsw snd_cdevsw = { 52987e5972SCameron Grant /* open */ sndopen, 53987e5972SCameron Grant /* close */ sndclose, 54987e5972SCameron Grant /* read */ sndread, 55987e5972SCameron Grant /* write */ sndwrite, 56987e5972SCameron Grant /* ioctl */ sndioctl, 57987e5972SCameron Grant /* poll */ sndpoll, 58987e5972SCameron Grant /* mmap */ sndmmap, 59987e5972SCameron Grant /* strategy */ nostrategy, 60987e5972SCameron Grant /* name */ "snd", 61987e5972SCameron Grant /* maj */ CDEV_MAJOR, 62987e5972SCameron Grant /* dump */ nodump, 63987e5972SCameron Grant /* psize */ nopsize, 6466ef8af5SCameron Grant /* flags */ D_TRACKCLOSE, 65987e5972SCameron Grant }; 66987e5972SCameron Grant 67dd186369SCameron Grant /* 68dd186369SCameron Grant PROPOSAL: 69987e5972SCameron Grant each unit needs: 70987e5972SCameron Grant status, mixer, dsp, dspW, audio, sequencer, midi-in, seq2, sndproc = 9 devices 71987e5972SCameron Grant dspW and audio are deprecated. 72987e5972SCameron Grant dsp needs min 64 channels, will give it 256 73987e5972SCameron Grant 74dd186369SCameron Grant minor = (unit << 20) + (dev << 16) + channel 75dd186369SCameron Grant currently minor = (channel << 16) + (unit << 4) + dev 76987e5972SCameron Grant 77987e5972SCameron Grant nomenclature: 78987e5972SCameron Grant /dev/pcmX/dsp.(0..255) 79987e5972SCameron Grant /dev/pcmX/dspW 80987e5972SCameron Grant /dev/pcmX/audio 81987e5972SCameron Grant /dev/pcmX/status 82987e5972SCameron Grant /dev/pcmX/mixer 83987e5972SCameron Grant [etc.] 84987e5972SCameron Grant */ 85987e5972SCameron Grant 86987e5972SCameron Grant #define PCMMINOR(x) (minor(x)) 87a618cffeSCameron Grant #define PCMCHAN(x) ((PCMMINOR(x) & 0x00ff0000) >> 16) 88987e5972SCameron Grant #define PCMUNIT(x) ((PCMMINOR(x) & 0x000000f0) >> 4) 89987e5972SCameron Grant #define PCMDEV(x) (PCMMINOR(x) & 0x0000000f) 90dd186369SCameron Grant #define PCMMKMINOR(u, d, c) ((((c) & 0xff) << 16) | (((u) & 0x0f) << 4) | ((d) & 0x0f)) 91987e5972SCameron Grant 92987e5972SCameron Grant static devclass_t pcm_devclass; 9382db23e2SCameron Grant 9482db23e2SCameron Grant #ifdef USING_DEVFS 9581930014SPeter Wemm static int snd_unit = 0; 964422746fSPeter Wemm TUNABLE_INT("hw.snd.unit", snd_unit); 9782db23e2SCameron Grant #endif 98987e5972SCameron Grant 9982db23e2SCameron Grant SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver"); 10082db23e2SCameron Grant 10137209180SCameron Grant void * 10237209180SCameron Grant snd_mtxcreate(const char *desc) 10337209180SCameron Grant { 10437209180SCameron Grant #ifdef USING_MUTEX 10537209180SCameron Grant struct mtx *m; 10637209180SCameron Grant 10737209180SCameron Grant m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); 10837209180SCameron Grant if (m == NULL) 10937209180SCameron Grant return NULL; 11037209180SCameron Grant mtx_init(m, desc, MTX_RECURSE); 11137209180SCameron Grant return m; 11237209180SCameron Grant #else 113a983d575SCameron Grant return (void *)0xcafebabe; 11437209180SCameron Grant #endif 11537209180SCameron Grant } 11637209180SCameron Grant 11737209180SCameron Grant void 11837209180SCameron Grant snd_mtxfree(void *m) 11937209180SCameron Grant { 12037209180SCameron Grant #ifdef USING_MUTEX 12137209180SCameron Grant struct mtx *mtx = m; 12237209180SCameron Grant 12337209180SCameron Grant mtx_assert(mtx, MA_OWNED); 12437209180SCameron Grant mtx_destroy(mtx); 12537209180SCameron Grant free(mtx, M_DEVBUF); 12637209180SCameron Grant #endif 12737209180SCameron Grant } 12837209180SCameron Grant 12937209180SCameron Grant void 13037209180SCameron Grant snd_mtxassert(void *m) 13137209180SCameron Grant { 13237209180SCameron Grant #ifdef USING_MUTEX 13337209180SCameron Grant struct mtx *mtx = m; 13437209180SCameron Grant 13537209180SCameron Grant mtx_assert(mtx, MA_OWNED); 13637209180SCameron Grant #endif 13737209180SCameron Grant } 13837209180SCameron Grant 13937209180SCameron Grant void 14037209180SCameron Grant snd_mtxlock(void *m) 14137209180SCameron Grant { 14237209180SCameron Grant #ifdef USING_MUTEX 14337209180SCameron Grant struct mtx *mtx = m; 14437209180SCameron Grant 14537209180SCameron Grant mtx_lock(mtx); 14637209180SCameron Grant #endif 14737209180SCameron Grant } 14837209180SCameron Grant 14937209180SCameron Grant void 15037209180SCameron Grant snd_mtxunlock(void *m) 15137209180SCameron Grant { 15237209180SCameron Grant #ifdef USING_MUTEX 15337209180SCameron Grant struct mtx *mtx = m; 15437209180SCameron Grant 15537209180SCameron Grant mtx_unlock(mtx); 15637209180SCameron Grant #endif 15737209180SCameron Grant } 15837209180SCameron Grant 15937209180SCameron Grant int 16037209180SCameron Grant snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) 16137209180SCameron Grant { 16237209180SCameron Grant #ifdef USING_MUTEX 16337209180SCameron Grant flags &= INTR_MPSAFE; 16437209180SCameron Grant flags |= INTR_TYPE_TTY; 16537209180SCameron Grant #else 16637209180SCameron Grant flags = INTR_TYPE_TTY; 16737209180SCameron Grant #endif 16837209180SCameron Grant return bus_setup_intr(dev, res, flags, hand, param, cookiep); 16937209180SCameron Grant } 17037209180SCameron Grant 171285648f9SCameron Grant struct pcm_channel * 172285648f9SCameron Grant pcm_chnalloc(struct snddev_info *d, int direction) 173285648f9SCameron Grant { 174285648f9SCameron Grant struct pcm_channel *c; 175285648f9SCameron Grant struct snddev_channel *sce; 176285648f9SCameron Grant 17749c5e6e2SCameron Grant snd_mtxlock(d->lock); 178285648f9SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 179285648f9SCameron Grant c = sce->channel; 18049c5e6e2SCameron Grant CHN_LOCK(c); 181285648f9SCameron Grant if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) { 182285648f9SCameron Grant c->flags |= CHN_F_BUSY; 18349c5e6e2SCameron Grant CHN_UNLOCK(c); 18449c5e6e2SCameron Grant snd_mtxunlock(d->lock); 185285648f9SCameron Grant return c; 186285648f9SCameron Grant } 18749c5e6e2SCameron Grant CHN_UNLOCK(c); 188285648f9SCameron Grant } 18949c5e6e2SCameron Grant snd_mtxunlock(d->lock); 190285648f9SCameron Grant return NULL; 191285648f9SCameron Grant } 192285648f9SCameron Grant 193285648f9SCameron Grant int 194285648f9SCameron Grant pcm_chnfree(struct pcm_channel *c) 195285648f9SCameron Grant { 19649c5e6e2SCameron Grant CHN_LOCK(c); 197285648f9SCameron Grant c->flags &= ~CHN_F_BUSY; 19849c5e6e2SCameron Grant CHN_UNLOCK(c); 199285648f9SCameron Grant return 0; 200285648f9SCameron Grant } 201285648f9SCameron Grant 202285648f9SCameron Grant int 203285648f9SCameron Grant pcm_chnref(struct pcm_channel *c, int ref) 204285648f9SCameron Grant { 20549c5e6e2SCameron Grant int r; 20649c5e6e2SCameron Grant 20749c5e6e2SCameron Grant CHN_LOCK(c); 208285648f9SCameron Grant c->refcount += ref; 20949c5e6e2SCameron Grant r = c->refcount; 21049c5e6e2SCameron Grant CHN_UNLOCK(c); 21149c5e6e2SCameron Grant return r; 212285648f9SCameron Grant } 213285648f9SCameron Grant 21482db23e2SCameron Grant #ifdef USING_DEVFS 21533dbf14aSCameron Grant static void 21633dbf14aSCameron Grant pcm_makelinks(void *dummy) 217987e5972SCameron Grant { 21833dbf14aSCameron Grant int unit; 21933dbf14aSCameron Grant dev_t pdev; 22033dbf14aSCameron Grant static dev_t dsp = 0, dspW = 0, audio = 0, mixer = 0; 221285648f9SCameron Grant struct snddev_info *d; 22233dbf14aSCameron Grant 223f776b5abSCameron Grant if (pcm_devclass == NULL || devfs_present == 0) 22433dbf14aSCameron Grant return; 22533dbf14aSCameron Grant if (dsp) { 22633dbf14aSCameron Grant destroy_dev(dsp); 22733dbf14aSCameron Grant dsp = 0; 228987e5972SCameron Grant } 22933dbf14aSCameron Grant if (dspW) { 23033dbf14aSCameron Grant destroy_dev(dspW); 23133dbf14aSCameron Grant dspW = 0; 23233dbf14aSCameron Grant } 23333dbf14aSCameron Grant if (audio) { 23433dbf14aSCameron Grant destroy_dev(audio); 23533dbf14aSCameron Grant audio = 0; 23633dbf14aSCameron Grant } 23733dbf14aSCameron Grant if (mixer) { 23833dbf14aSCameron Grant destroy_dev(mixer); 23933dbf14aSCameron Grant mixer = 0; 24033dbf14aSCameron Grant } 24133dbf14aSCameron Grant 24233dbf14aSCameron Grant unit = snd_unit; 24333dbf14aSCameron Grant if (unit < 0 || unit > devclass_get_maxunit(pcm_devclass)) 24433dbf14aSCameron Grant return; 245285648f9SCameron Grant d = devclass_get_softc(pcm_devclass, unit); 246285648f9SCameron Grant if (d == NULL || d->chancount == 0) 24733dbf14aSCameron Grant return; 24833dbf14aSCameron Grant 24933dbf14aSCameron Grant pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, 0)); 25033dbf14aSCameron Grant dsp = make_dev_alias(pdev, "dsp"); 25133dbf14aSCameron Grant pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, 0)); 25233dbf14aSCameron Grant dspW = make_dev_alias(pdev, "dspW"); 25333dbf14aSCameron Grant pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, 0)); 25433dbf14aSCameron Grant audio = make_dev_alias(pdev, "audio"); 25533dbf14aSCameron Grant pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_CTL, 0)); 25633dbf14aSCameron Grant mixer = make_dev_alias(pdev, "mixer"); 25733dbf14aSCameron Grant } 25833dbf14aSCameron Grant 25933dbf14aSCameron Grant static int 26033dbf14aSCameron Grant sysctl_hw_sndunit(SYSCTL_HANDLER_ARGS) 26133dbf14aSCameron Grant { 26233dbf14aSCameron Grant int error, unit; 26333dbf14aSCameron Grant 26433dbf14aSCameron Grant unit = snd_unit; 26533dbf14aSCameron Grant error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); 26633dbf14aSCameron Grant if (error == 0 && req->newptr != NULL) { 26733dbf14aSCameron Grant snd_unit = unit; 26833dbf14aSCameron Grant pcm_makelinks(NULL); 26933dbf14aSCameron Grant } 27033dbf14aSCameron Grant return (error); 27133dbf14aSCameron Grant } 272b3b7ccfeSJohn Baldwin SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW, 27333dbf14aSCameron Grant 0, sizeof(int), sysctl_hw_sndunit, "I", ""); 27482db23e2SCameron Grant #endif 275987e5972SCameron Grant 276285648f9SCameron Grant struct pcm_channel * 277285648f9SCameron Grant pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo) 278987e5972SCameron Grant { 279285648f9SCameron Grant struct pcm_channel *ch; 28033dbf14aSCameron Grant char *dirs; 2810f55ac6cSCameron Grant int err; 282987e5972SCameron Grant 283285648f9SCameron Grant switch(dir) { 284285648f9SCameron Grant case PCMDIR_PLAY: 285285648f9SCameron Grant dirs = "play"; 286285648f9SCameron Grant break; 287285648f9SCameron Grant case PCMDIR_REC: 288285648f9SCameron Grant dirs = "record"; 289285648f9SCameron Grant break; 290285648f9SCameron Grant case PCMDIR_VIRTUAL: 291285648f9SCameron Grant dirs = "virtual"; 292285648f9SCameron Grant dir = PCMDIR_PLAY; 293285648f9SCameron Grant break; 294285648f9SCameron Grant default: 295285648f9SCameron Grant return NULL; 2969c326820SCameron Grant } 297285648f9SCameron Grant 298285648f9SCameron Grant ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 299285648f9SCameron Grant if (!ch) 300285648f9SCameron Grant return NULL; 301285648f9SCameron Grant 3020f55ac6cSCameron Grant ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK); 303285648f9SCameron Grant if (!ch->methods) { 304285648f9SCameron Grant free(ch, M_DEVBUF); 305285648f9SCameron Grant return NULL; 306285648f9SCameron Grant } 307285648f9SCameron Grant 308285648f9SCameron Grant ch->pid = -1; 309285648f9SCameron Grant ch->parentsnddev = d; 310285648f9SCameron Grant ch->parentchannel = parent; 311285648f9SCameron Grant snprintf(ch->name, 32, "%s:%d:%s", device_get_nameunit(d->dev), d->chancount, dirs); 312285648f9SCameron Grant 3130f55ac6cSCameron Grant err = chn_init(ch, devinfo, dir); 3140f55ac6cSCameron Grant if (err) { 315285648f9SCameron Grant device_printf(d->dev, "chn_init() for channel %d (%s) failed: err = %d\n", d->chancount, dirs, err); 316285648f9SCameron Grant kobj_delete(ch->methods, M_DEVBUF); 317285648f9SCameron Grant free(ch, M_DEVBUF); 318285648f9SCameron Grant return NULL; 319bbb5bf3dSCameron Grant } 320285648f9SCameron Grant 321285648f9SCameron Grant return ch; 322285648f9SCameron Grant } 323285648f9SCameron Grant 324285648f9SCameron Grant int 325285648f9SCameron Grant pcm_chn_destroy(struct pcm_channel *ch) 326285648f9SCameron Grant { 327285648f9SCameron Grant int err; 328285648f9SCameron Grant 329285648f9SCameron Grant err = chn_kill(ch); 330285648f9SCameron Grant if (err) { 331285648f9SCameron Grant device_printf(ch->parentsnddev->dev, "chn_kill() for %s failed, err = %d\n", ch->name, err); 332285648f9SCameron Grant return err; 333285648f9SCameron Grant } 334285648f9SCameron Grant 335285648f9SCameron Grant kobj_delete(ch->methods, M_DEVBUF); 336285648f9SCameron Grant free(ch, M_DEVBUF); 337285648f9SCameron Grant 338285648f9SCameron Grant return 0; 339285648f9SCameron Grant } 340285648f9SCameron Grant 341285648f9SCameron Grant int 342285648f9SCameron Grant pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) 343285648f9SCameron Grant { 344285648f9SCameron Grant struct snddev_channel *sce; 345285648f9SCameron Grant int unit = device_get_unit(d->dev); 346285648f9SCameron Grant 347285648f9SCameron Grant sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO); 348285648f9SCameron Grant if (!sce) { 349285648f9SCameron Grant free(ch, M_DEVBUF); 350285648f9SCameron Grant return ENOMEM; 351285648f9SCameron Grant } 352285648f9SCameron Grant 35349c5e6e2SCameron Grant snd_mtxlock(d->lock); 354285648f9SCameron Grant sce->channel = ch; 355285648f9SCameron Grant SLIST_INSERT_HEAD(&d->channels, sce, link); 356285648f9SCameron Grant 3577207eca6SCameron Grant make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount), 3587207eca6SCameron Grant UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", unit, d->chancount); 3597207eca6SCameron Grant make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount), 3607207eca6SCameron Grant UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", unit, d->chancount); 36133dbf14aSCameron Grant make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount), 36233dbf14aSCameron Grant UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", unit, d->chancount); 3637207eca6SCameron Grant /* XXX SND_DEV_NORESET? */ 364285648f9SCameron Grant 36582db23e2SCameron Grant #ifdef USING_DEVFS 366285648f9SCameron Grant if (d->chancount++ == 0) 36733dbf14aSCameron Grant pcm_makelinks(NULL); 36882db23e2SCameron Grant #endif 36949c5e6e2SCameron Grant snd_mtxunlock(d->lock); 370285648f9SCameron Grant 37133dbf14aSCameron Grant return 0; 37233dbf14aSCameron Grant } 37333dbf14aSCameron Grant 374285648f9SCameron Grant int 375285648f9SCameron Grant pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch) 37633dbf14aSCameron Grant { 377285648f9SCameron Grant struct snddev_channel *sce; 378285648f9SCameron Grant int unit = device_get_unit(d->dev); 37933dbf14aSCameron Grant dev_t pdev; 38033dbf14aSCameron Grant 38149c5e6e2SCameron Grant snd_mtxlock(d->lock); 382285648f9SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 383285648f9SCameron Grant if (sce->channel == ch) 384285648f9SCameron Grant goto gotit; 38533dbf14aSCameron Grant } 38649c5e6e2SCameron Grant snd_mtxunlock(d->lock); 387285648f9SCameron Grant return EINVAL; 388285648f9SCameron Grant gotit: 38933dbf14aSCameron Grant d->chancount--; 390285648f9SCameron Grant SLIST_REMOVE(&d->channels, sce, snddev_channel, link); 391285648f9SCameron Grant free(sce, M_DEVBUF); 392285648f9SCameron Grant 393285648f9SCameron Grant #ifdef USING_DEVFS 394285648f9SCameron Grant if (d->chancount == 0) 395285648f9SCameron Grant pcm_makelinks(NULL); 396285648f9SCameron Grant #endif 39733dbf14aSCameron Grant pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount)); 39833dbf14aSCameron Grant destroy_dev(pdev); 39933dbf14aSCameron Grant pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount)); 40033dbf14aSCameron Grant destroy_dev(pdev); 40133dbf14aSCameron Grant pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount)); 40233dbf14aSCameron Grant destroy_dev(pdev); 40349c5e6e2SCameron Grant snd_mtxunlock(d->lock); 404285648f9SCameron Grant 405987e5972SCameron Grant return 0; 406987e5972SCameron Grant } 407987e5972SCameron Grant 408987e5972SCameron Grant int 409285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 410285648f9SCameron Grant { 411285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 412285648f9SCameron Grant struct pcm_channel *ch; 413285648f9SCameron Grant int err; 414285648f9SCameron Grant 415285648f9SCameron Grant ch = pcm_chn_create(d, NULL, cls, dir, devinfo); 416285648f9SCameron Grant if (!ch) { 417285648f9SCameron Grant device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo); 418285648f9SCameron Grant return ENODEV; 419285648f9SCameron Grant } 420285648f9SCameron Grant err = pcm_chn_add(d, ch); 421285648f9SCameron Grant if (err) { 422285648f9SCameron Grant device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err); 423285648f9SCameron Grant pcm_chn_destroy(ch); 424285648f9SCameron Grant } 425285648f9SCameron Grant 426285648f9SCameron Grant return err; 427285648f9SCameron Grant } 428285648f9SCameron Grant 429285648f9SCameron Grant static int 430285648f9SCameron Grant pcm_killchan(device_t dev) 431285648f9SCameron Grant { 432285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 433285648f9SCameron Grant struct snddev_channel *sce; 434285648f9SCameron Grant 43549c5e6e2SCameron Grant snd_mtxlock(d->lock); 436285648f9SCameron Grant sce = SLIST_FIRST(&d->channels); 43749c5e6e2SCameron Grant snd_mtxunlock(d->lock); 438285648f9SCameron Grant 439285648f9SCameron Grant return pcm_chn_remove(d, sce->channel); 440285648f9SCameron Grant } 441285648f9SCameron Grant 442285648f9SCameron Grant int 443987e5972SCameron Grant pcm_setstatus(device_t dev, char *str) 444987e5972SCameron Grant { 44566ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 44649c5e6e2SCameron Grant 44749c5e6e2SCameron Grant snd_mtxlock(d->lock); 448987e5972SCameron Grant strncpy(d->status, str, SND_STATUSLEN); 44949c5e6e2SCameron Grant snd_mtxunlock(d->lock); 450987e5972SCameron Grant return 0; 451987e5972SCameron Grant } 452987e5972SCameron Grant 453987e5972SCameron Grant u_int32_t 454987e5972SCameron Grant pcm_getflags(device_t dev) 455987e5972SCameron Grant { 45666ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 45749c5e6e2SCameron Grant 458987e5972SCameron Grant return d->flags; 459987e5972SCameron Grant } 460987e5972SCameron Grant 461987e5972SCameron Grant void 462987e5972SCameron Grant pcm_setflags(device_t dev, u_int32_t val) 463987e5972SCameron Grant { 46466ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 46549c5e6e2SCameron Grant 466987e5972SCameron Grant d->flags = val; 467987e5972SCameron Grant } 468987e5972SCameron Grant 46939004e69SCameron Grant void * 47039004e69SCameron Grant pcm_getdevinfo(device_t dev) 47139004e69SCameron Grant { 47266ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 47349c5e6e2SCameron Grant 47439004e69SCameron Grant return d->devinfo; 47539004e69SCameron Grant } 47639004e69SCameron Grant 477987e5972SCameron Grant /* This is the generic init routine */ 478987e5972SCameron Grant int 479987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 480987e5972SCameron Grant { 481987e5972SCameron Grant int sz, unit = device_get_unit(dev); 48266ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 483987e5972SCameron Grant 48449c5e6e2SCameron Grant d->lock = snd_mtxcreate(device_get_nameunit(dev)); 48549c5e6e2SCameron Grant snd_mtxlock(d->lock); 486987e5972SCameron Grant if (!pcm_devclass) { 487987e5972SCameron Grant pcm_devclass = device_get_devclass(dev); 48833dbf14aSCameron Grant status_dev = make_dev(&snd_cdevsw, PCMMKMINOR(0, SND_DEV_STATUS, 0), 489083279e4SSeigo Tanimura UID_ROOT, GID_WHEEL, 0444, "sndstat"); 490083279e4SSeigo Tanimura } 491285648f9SCameron Grant 4927207eca6SCameron Grant make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0), 49311346231SSeigo Tanimura UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit); 494285648f9SCameron Grant 495e4d5b250SCameron Grant d->dev = dev; 496987e5972SCameron Grant d->devinfo = devinfo; 497285648f9SCameron Grant d->chancount = 0; 49833dbf14aSCameron Grant d->maxchans = numplay + numrec; 499285648f9SCameron Grant sz = d->maxchans * sizeof(struct pcm_channel *); 500833f7023SCameron Grant 501833f7023SCameron Grant if (sz > 0) { 502285648f9SCameron Grant d->aplay = (struct pcm_channel **)malloc(sz, M_DEVBUF, M_WAITOK | M_ZERO); 503285648f9SCameron Grant d->arec = (struct pcm_channel **)malloc(sz, M_DEVBUF, M_WAITOK | M_ZERO); 504285648f9SCameron Grant if (!d->arec || !d->aplay) goto no; 505833f7023SCameron Grant 506285648f9SCameron Grant if (numplay == 0 || numrec == 0) 507285648f9SCameron Grant d->flags |= SD_F_SIMPLEX; 508bd18f334SCameron Grant 509285648f9SCameron Grant d->fakechan = fkchan_setup(dev); 510285648f9SCameron Grant chn_init(d->fakechan, NULL, 0); 511833f7023SCameron Grant } 512987e5972SCameron Grant 51382db23e2SCameron Grant #ifdef SND_DYNSYSCTL 514cc486d80SJohn Baldwin sysctl_ctx_init(&d->sysctl_tree); 515cc486d80SJohn Baldwin d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree, 516a3e893e0SJohn Baldwin SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO, 517cc486d80SJohn Baldwin device_get_nameunit(dev), CTLFLAG_RD, 0, ""); 518a3e893e0SJohn Baldwin if (d->sysctl_tree_top == NULL) { 519cc486d80SJohn Baldwin sysctl_ctx_free(&d->sysctl_tree); 520cc486d80SJohn Baldwin goto no; 521cc486d80SJohn Baldwin } 52282db23e2SCameron Grant #endif 52349c5e6e2SCameron Grant #if 1 524285648f9SCameron Grant vchan_initsys(d); 525285648f9SCameron Grant #endif 52649c5e6e2SCameron Grant snd_mtxunlock(d->lock); 527987e5972SCameron Grant return 0; 528987e5972SCameron Grant no: 529987e5972SCameron Grant if (d->aplay) free(d->aplay, M_DEVBUF); 530987e5972SCameron Grant if (d->arec) free(d->arec, M_DEVBUF); 53149c5e6e2SCameron Grant /* snd_mtxunlock(d->lock); */ 53249c5e6e2SCameron Grant snd_mtxfree(d->lock); 533987e5972SCameron Grant return ENXIO; 534987e5972SCameron Grant } 535987e5972SCameron Grant 53633dbf14aSCameron Grant int 53733dbf14aSCameron Grant pcm_unregister(device_t dev) 5387c438dbeSCameron Grant { 539285648f9SCameron Grant int unit = device_get_unit(dev); 54066ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 541285648f9SCameron Grant struct snddev_channel *sce; 5427c438dbeSCameron Grant dev_t pdev; 5437c438dbeSCameron Grant 54449c5e6e2SCameron Grant snd_mtxlock(d->lock); 545285648f9SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 546285648f9SCameron Grant if (sce->channel->refcount > 0) { 547c9b53085SCameron Grant device_printf(dev, "unregister: channel busy"); 54849c5e6e2SCameron Grant snd_mtxunlock(d->lock); 549285648f9SCameron Grant return EBUSY; 550285648f9SCameron Grant } 551c9b53085SCameron Grant } 5520f55ac6cSCameron Grant if (mixer_isbusy(d->mixer)) { 553c9b53085SCameron Grant device_printf(dev, "unregister: mixer busy"); 55449c5e6e2SCameron Grant snd_mtxunlock(d->lock); 555c9b53085SCameron Grant return EBUSY; 556c9b53085SCameron Grant } 5577c438dbeSCameron Grant 55866ef8af5SCameron Grant #ifdef SND_DYNSYSCTL 55966ef8af5SCameron Grant d->sysctl_tree_top = NULL; 56066ef8af5SCameron Grant sysctl_ctx_free(&d->sysctl_tree); 56166ef8af5SCameron Grant #endif 56266ef8af5SCameron Grant 5637c438dbeSCameron Grant pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_CTL, 0)); 56433dbf14aSCameron Grant destroy_dev(pdev); 56533dbf14aSCameron Grant mixer_uninit(dev); 5667c438dbeSCameron Grant 567285648f9SCameron Grant while (d->chancount > 0) 568285648f9SCameron Grant pcm_killchan(dev); 5697c438dbeSCameron Grant 57033dbf14aSCameron Grant if (d->aplay) free(d->aplay, M_DEVBUF); 57133dbf14aSCameron Grant if (d->arec) free(d->arec, M_DEVBUF); 57233dbf14aSCameron Grant 57366ef8af5SCameron Grant chn_kill(d->fakechan); 57466ef8af5SCameron Grant fkchan_kill(d->fakechan); 57582db23e2SCameron Grant 57649c5e6e2SCameron Grant /* snd_mtxunlock(d->lock); */ 57749c5e6e2SCameron Grant snd_mtxfree(d->lock); 57833dbf14aSCameron Grant return 0; 57933dbf14aSCameron Grant } 5807c438dbeSCameron Grant 581987e5972SCameron Grant /* 582987e5972SCameron Grant * a small utility function which, given a device number, returns 58366ef8af5SCameron Grant * a pointer to the associated struct snddev_info struct, and sets the unit 584987e5972SCameron Grant * number. 585987e5972SCameron Grant */ 58666ef8af5SCameron Grant static struct snddev_info * 587987e5972SCameron Grant get_snddev_info(dev_t i_dev, int *unit, int *dev, int *chan) 588987e5972SCameron Grant { 58966ef8af5SCameron Grant struct snddev_info *sc; 590987e5972SCameron Grant int u, d, c; 591987e5972SCameron Grant 592987e5972SCameron Grant u = PCMUNIT(i_dev); 593987e5972SCameron Grant d = PCMDEV(i_dev); 594987e5972SCameron Grant c = PCMCHAN(i_dev); 595987e5972SCameron Grant if (u > devclass_get_maxunit(pcm_devclass)) u = -1; 596987e5972SCameron Grant if (unit) *unit = u; 597987e5972SCameron Grant if (dev) *dev = d; 598987e5972SCameron Grant if (chan) *chan = c; 599987e5972SCameron Grant if (u < 0) return NULL; 600987e5972SCameron Grant 60133dbf14aSCameron Grant sc = devclass_get_softc(pcm_devclass, u); 60266ef8af5SCameron Grant if (sc == NULL) return NULL; 60333dbf14aSCameron Grant 604987e5972SCameron Grant switch(d) { 605987e5972SCameron Grant case SND_DEV_CTL: /* /dev/mixer handled by pcm */ 606987e5972SCameron Grant case SND_DEV_STATUS: /* /dev/sndstat handled by pcm */ 607987e5972SCameron Grant case SND_DEV_DSP: 608987e5972SCameron Grant case SND_DEV_DSP16: 609987e5972SCameron Grant case SND_DEV_AUDIO: 61033dbf14aSCameron Grant return sc; 611987e5972SCameron Grant 612987e5972SCameron Grant case SND_DEV_SEQ: /* XXX when enabled... */ 613987e5972SCameron Grant case SND_DEV_SEQ2: 614987e5972SCameron Grant case SND_DEV_MIDIN: 615987e5972SCameron Grant case SND_DEV_SNDPROC: /* /dev/sndproc handled by pcm */ 616987e5972SCameron Grant default: 617987e5972SCameron Grant printf("unsupported subdevice %d\n", d); 618987e5972SCameron Grant return NULL; 619987e5972SCameron Grant } 620987e5972SCameron Grant } 621987e5972SCameron Grant 622987e5972SCameron Grant static int 623987e5972SCameron Grant sndopen(dev_t i_dev, int flags, int mode, struct proc *p) 624987e5972SCameron Grant { 625987e5972SCameron Grant int dev, unit, chan; 62666ef8af5SCameron Grant struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 627987e5972SCameron Grant 628987e5972SCameron Grant DEB(printf("open snd%d subdev %d flags 0x%08x mode 0x%08x\n", 629987e5972SCameron Grant unit, dev, flags, mode)); 630987e5972SCameron Grant 631987e5972SCameron Grant switch(dev) { 632987e5972SCameron Grant case SND_DEV_STATUS: 633285648f9SCameron Grant return do_status(0, NULL); 634987e5972SCameron Grant 635987e5972SCameron Grant case SND_DEV_CTL: 6360f55ac6cSCameron Grant return d? mixer_busy(d->mixer, 1) : ENXIO; 637987e5972SCameron Grant 638987e5972SCameron Grant case SND_DEV_AUDIO: 639987e5972SCameron Grant case SND_DEV_DSP: 640987e5972SCameron Grant case SND_DEV_DSP16: 6415b78a734SCameron Grant case SND_DEV_NORESET: 642285648f9SCameron Grant return d? dsp_open(d, chan, flags, dev, p->p_pid) : ENXIO; 643987e5972SCameron Grant 644987e5972SCameron Grant default: 645987e5972SCameron Grant return ENXIO; 646987e5972SCameron Grant } 647987e5972SCameron Grant } 648987e5972SCameron Grant 649987e5972SCameron Grant static int 650987e5972SCameron Grant sndclose(dev_t i_dev, int flags, int mode, struct proc *p) 651987e5972SCameron Grant { 652987e5972SCameron Grant int dev, unit, chan; 65366ef8af5SCameron Grant struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 654987e5972SCameron Grant 655987e5972SCameron Grant DEB(printf("close snd%d subdev %d\n", unit, dev)); 656987e5972SCameron Grant 657987e5972SCameron Grant switch(dev) { /* only those for which close makes sense */ 658987e5972SCameron Grant case SND_DEV_STATUS: 659285648f9SCameron Grant return do_status(1, NULL); 660987e5972SCameron Grant 661987e5972SCameron Grant case SND_DEV_CTL: 6620f55ac6cSCameron Grant return d? mixer_busy(d->mixer, 0) : ENXIO; 663987e5972SCameron Grant 664987e5972SCameron Grant case SND_DEV_AUDIO: 665987e5972SCameron Grant case SND_DEV_DSP: 666987e5972SCameron Grant case SND_DEV_DSP16: 667987e5972SCameron Grant return d? dsp_close(d, chan, dev) : ENXIO; 668987e5972SCameron Grant 669987e5972SCameron Grant default: 670987e5972SCameron Grant return ENXIO; 671987e5972SCameron Grant } 672987e5972SCameron Grant } 673987e5972SCameron Grant 674987e5972SCameron Grant static int 675987e5972SCameron Grant sndread(dev_t i_dev, struct uio *buf, int flag) 676987e5972SCameron Grant { 677987e5972SCameron Grant int dev, unit, chan; 67866ef8af5SCameron Grant struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 679987e5972SCameron Grant DEB(printf("read snd%d subdev %d flag 0x%08x\n", unit, dev, flag)); 680987e5972SCameron Grant 681987e5972SCameron Grant switch(dev) { 682987e5972SCameron Grant case SND_DEV_STATUS: 683285648f9SCameron Grant return do_status(2, buf); 684987e5972SCameron Grant 685987e5972SCameron Grant case SND_DEV_AUDIO: 686987e5972SCameron Grant case SND_DEV_DSP: 687987e5972SCameron Grant case SND_DEV_DSP16: 688987e5972SCameron Grant return d? dsp_read(d, chan, buf, flag) : EBADF; 689987e5972SCameron Grant 690987e5972SCameron Grant default: 691987e5972SCameron Grant return ENXIO; 692987e5972SCameron Grant } 693987e5972SCameron Grant } 694987e5972SCameron Grant 695987e5972SCameron Grant static int 696987e5972SCameron Grant sndwrite(dev_t i_dev, struct uio *buf, int flag) 697987e5972SCameron Grant { 698987e5972SCameron Grant int dev, unit, chan; 69966ef8af5SCameron Grant struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 700987e5972SCameron Grant 701987e5972SCameron Grant DEB(printf("write snd%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag)); 702987e5972SCameron Grant 703987e5972SCameron Grant switch(dev) { /* only writeable devices */ 704987e5972SCameron Grant case SND_DEV_DSP: 705987e5972SCameron Grant case SND_DEV_DSP16: 706987e5972SCameron Grant case SND_DEV_AUDIO: 707987e5972SCameron Grant return d? dsp_write(d, chan, buf, flag) : EBADF; 708987e5972SCameron Grant 709987e5972SCameron Grant default: 710987e5972SCameron Grant return EPERM; /* for non-writeable devices ; */ 711987e5972SCameron Grant } 712987e5972SCameron Grant } 713987e5972SCameron Grant 714987e5972SCameron Grant static int 715987e5972SCameron Grant sndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p) 716987e5972SCameron Grant { 717987e5972SCameron Grant int dev, chan; 71866ef8af5SCameron Grant struct snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan); 719987e5972SCameron Grant 720987e5972SCameron Grant if (d == NULL) return ENXIO; 721987e5972SCameron Grant 722987e5972SCameron Grant switch(dev) { 723987e5972SCameron Grant case SND_DEV_CTL: 724987e5972SCameron Grant return mixer_ioctl(d, cmd, arg); 725987e5972SCameron Grant 726987e5972SCameron Grant case SND_DEV_AUDIO: 727987e5972SCameron Grant case SND_DEV_DSP: 728987e5972SCameron Grant case SND_DEV_DSP16: 7291ad869dbSCameron Grant if (IOCGROUP(cmd) == 'M') 7301ad869dbSCameron Grant return mixer_ioctl(d, cmd, arg); 7311ad869dbSCameron Grant else 732987e5972SCameron Grant return dsp_ioctl(d, chan, cmd, arg); 733987e5972SCameron Grant 734987e5972SCameron Grant default: 735987e5972SCameron Grant return ENXIO; 736987e5972SCameron Grant } 737987e5972SCameron Grant } 738987e5972SCameron Grant 739987e5972SCameron Grant static int 740987e5972SCameron Grant sndpoll(dev_t i_dev, int events, struct proc *p) 741987e5972SCameron Grant { 742987e5972SCameron Grant int dev, chan; 74366ef8af5SCameron Grant struct snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan); 744987e5972SCameron Grant 7450e25481fSCameron Grant DEB(printf("sndpoll d 0x%p dev 0x%04x events 0x%08x\n", d, dev, events)); 746987e5972SCameron Grant 747987e5972SCameron Grant if (d == NULL) return ENXIO; 748987e5972SCameron Grant 749987e5972SCameron Grant switch(dev) { 750987e5972SCameron Grant case SND_DEV_AUDIO: 751987e5972SCameron Grant case SND_DEV_DSP: 752987e5972SCameron Grant case SND_DEV_DSP16: 753987e5972SCameron Grant return dsp_poll(d, chan, events, p); 754987e5972SCameron Grant 755987e5972SCameron Grant default: 756987e5972SCameron Grant return (events & 757987e5972SCameron Grant (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)) | POLLHUP; 758987e5972SCameron Grant } 759987e5972SCameron Grant } 760987e5972SCameron Grant 761987e5972SCameron Grant /* 762987e5972SCameron Grant * The mmap interface allows access to the play and read buffer, 763987e5972SCameron Grant * plus the device descriptor. 764987e5972SCameron Grant * The various blocks are accessible at the following offsets: 765987e5972SCameron Grant * 766987e5972SCameron Grant * 0x00000000 ( 0 ) : write buffer ; 767987e5972SCameron Grant * 0x01000000 (16 MB) : read buffer ; 768987e5972SCameron Grant * 0x02000000 (32 MB) : device descriptor (dangerous!) 769987e5972SCameron Grant * 770987e5972SCameron Grant * WARNING: the mmap routines assume memory areas are aligned. This 771987e5972SCameron Grant * is true (probably) for the dma buffers, but likely false for the 772987e5972SCameron Grant * device descriptor. As a consequence, we do not know where it is 773987e5972SCameron Grant * located in the requested area. 774987e5972SCameron Grant */ 775987e5972SCameron Grant static int 776987e5972SCameron Grant sndmmap(dev_t i_dev, vm_offset_t offset, int nprot) 777987e5972SCameron Grant { 778987e5972SCameron Grant int unit, dev, chan; 77966ef8af5SCameron Grant struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 780987e5972SCameron Grant 781987e5972SCameron Grant DEB(printf("sndmmap d 0x%p dev 0x%04x ofs 0x%08x nprot 0x%08x\n", 782987e5972SCameron Grant d, dev, offset, nprot)); 783987e5972SCameron Grant 784987e5972SCameron Grant if (d == NULL || nprot & PROT_EXEC) return -1; /* forbidden */ 785987e5972SCameron Grant 786987e5972SCameron Grant switch(dev) { 787987e5972SCameron Grant case SND_DEV_AUDIO: 788987e5972SCameron Grant case SND_DEV_DSP: 789987e5972SCameron Grant case SND_DEV_DSP16: 790987e5972SCameron Grant return dsp_mmap(d, chan, offset, nprot); 791987e5972SCameron Grant 792987e5972SCameron Grant default: 793987e5972SCameron Grant return -1; 794987e5972SCameron Grant } 795987e5972SCameron Grant } 796987e5972SCameron Grant 797987e5972SCameron Grant static int 798285648f9SCameron Grant status_init(struct sbuf *s) 799987e5972SCameron Grant { 800285648f9SCameron Grant int i, pc, rc, vc; 801987e5972SCameron Grant device_t dev; 80266ef8af5SCameron Grant struct snddev_info *d; 803285648f9SCameron Grant struct snddev_channel *sce; 804285648f9SCameron Grant struct pcm_channel *c; 80549c5e6e2SCameron Grant #ifdef SNDSTAT_VERBOSE 806285648f9SCameron Grant struct pcm_feeder *f; 80749c5e6e2SCameron Grant #endif 808987e5972SCameron Grant 809285648f9SCameron Grant sbuf_printf(s, "FreeBSD Audio Driver (newpcm) %s %s\nInstalled devices:\n", 810285648f9SCameron Grant __DATE__, __TIME__); 811987e5972SCameron Grant 812987e5972SCameron Grant for (i = 0; i <= devclass_get_maxunit(pcm_devclass); i++) { 81333dbf14aSCameron Grant d = devclass_get_softc(pcm_devclass, i); 814285648f9SCameron Grant if (!d) 815285648f9SCameron Grant continue; 81649c5e6e2SCameron Grant snd_mtxlock(d->lock); 817987e5972SCameron Grant dev = devclass_get_device(pcm_devclass, i); 818285648f9SCameron Grant sbuf_printf(s, "pcm%d: <%s> %s", i, device_get_desc(dev), d->status); 819285648f9SCameron Grant if (d->chancount > 0) { 820285648f9SCameron Grant pc = rc = vc = 0; 821285648f9SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 822285648f9SCameron Grant c = sce->channel; 823285648f9SCameron Grant if (c->direction == PCMDIR_PLAY) { 824285648f9SCameron Grant if (c->flags & CHN_F_VIRTUAL) 825285648f9SCameron Grant vc++; 826bf8ca271SCameron Grant else 827285648f9SCameron Grant pc++; 828285648f9SCameron Grant } else 829285648f9SCameron Grant rc++; 830bf8ca271SCameron Grant } 831285648f9SCameron Grant sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)\n", pc, rc, vc, 832285648f9SCameron Grant (d->flags & SD_F_SIMPLEX)? "" : " duplex", 833285648f9SCameron Grant #ifdef USING_DEVFS 834285648f9SCameron Grant (i == snd_unit)? " default" : "" 835285648f9SCameron Grant #else 836285648f9SCameron Grant "" 837285648f9SCameron Grant #endif 838285648f9SCameron Grant ); 839285648f9SCameron Grant #ifdef SNDSTAT_VERBOSE 840285648f9SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 841285648f9SCameron Grant c = sce->channel; 842285648f9SCameron Grant sbuf_printf(s, "\t%s[%s]: speed %d, format %08x, flags %08x", 843285648f9SCameron Grant c->parentchannel? c->parentchannel->name : "", 844285648f9SCameron Grant c->name, c->speed, c->format, c->flags); 845285648f9SCameron Grant if (c->pid != -1) 846285648f9SCameron Grant sbuf_printf(s, ", pid %d", c->pid); 847285648f9SCameron Grant sbuf_printf(s, "\n\t"); 848285648f9SCameron Grant f = c->feeder; 849285648f9SCameron Grant while (f) { 850285648f9SCameron Grant sbuf_printf(s, "%s", f->class->name); 851285648f9SCameron Grant if (f->desc->type == FEEDER_FMT) 852285648f9SCameron Grant sbuf_printf(s, "(%08x <- %08x)", f->desc->out, f->desc->in); 853285648f9SCameron Grant if (f->desc->type == FEEDER_RATE) 854285648f9SCameron Grant sbuf_printf(s, "(%d <- %d)", FEEDER_GET(f, FEEDRATE_DST), FEEDER_GET(f, FEEDRATE_SRC)); 855285648f9SCameron Grant if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER) 856285648f9SCameron Grant sbuf_printf(s, "(%08x)", f->desc->out); 857285648f9SCameron Grant if (f->source) 858285648f9SCameron Grant sbuf_printf(s, " <- "); 859285648f9SCameron Grant f = f->source; 860987e5972SCameron Grant } 861285648f9SCameron Grant sbuf_printf(s, "\n"); 862285648f9SCameron Grant } 863285648f9SCameron Grant #endif 864285648f9SCameron Grant } else 865285648f9SCameron Grant sbuf_printf(s, " (mixer only)\n"); 86649c5e6e2SCameron Grant snd_mtxunlock(d->lock); 867285648f9SCameron Grant } 868285648f9SCameron Grant sbuf_finish(s); 869285648f9SCameron Grant return sbuf_len(s); 870987e5972SCameron Grant } 871987e5972SCameron Grant 872987e5972SCameron Grant static int 873285648f9SCameron Grant do_status(int action, struct uio *buf) 874987e5972SCameron Grant { 875285648f9SCameron Grant static struct sbuf s; 876285648f9SCameron Grant static int bufptr = 0; 877285648f9SCameron Grant static int status_open = 0; 878285648f9SCameron Grant int l, err; 879987e5972SCameron Grant 880285648f9SCameron Grant switch(action) { 881285648f9SCameron Grant case 0: /* open */ 882285648f9SCameron Grant if (status_open) 883285648f9SCameron Grant return EBUSY; 884285648f9SCameron Grant if (sbuf_new(&s, NULL, 4096, 0)) 885285648f9SCameron Grant return ENXIO; 886987e5972SCameron Grant bufptr = 0; 887285648f9SCameron Grant err = (status_init(&s) > 0)? 0 : ENOMEM; 888285648f9SCameron Grant if (!err) 889285648f9SCameron Grant status_open = 1; 890285648f9SCameron Grant return err; 891285648f9SCameron Grant 892285648f9SCameron Grant case 1: /* close */ 893285648f9SCameron Grant if (!status_open) 894285648f9SCameron Grant return EBADF; 895285648f9SCameron Grant sbuf_delete(&s); 896285648f9SCameron Grant status_open = 0; 897285648f9SCameron Grant return 0; 898285648f9SCameron Grant 899285648f9SCameron Grant case 2: 900285648f9SCameron Grant if (!status_open) 901285648f9SCameron Grant return EBADF; 902285648f9SCameron Grant l = min(buf->uio_resid, sbuf_len(&s) - bufptr); 903285648f9SCameron Grant err = (l > 0)? uiomove(sbuf_data(&s) + bufptr, l, buf) : 0; 904285648f9SCameron Grant bufptr += l; 905285648f9SCameron Grant return err; 906285648f9SCameron Grant 907285648f9SCameron Grant case 3: 908285648f9SCameron Grant return status_open; 909987e5972SCameron Grant } 910987e5972SCameron Grant 911285648f9SCameron Grant return EBADF; 912987e5972SCameron Grant } 91333dbf14aSCameron Grant 91433dbf14aSCameron Grant static int 91533dbf14aSCameron Grant sndpcm_modevent(module_t mod, int type, void *data) 91633dbf14aSCameron Grant { 91733dbf14aSCameron Grant 91833dbf14aSCameron Grant switch (type) { 91933dbf14aSCameron Grant case MOD_LOAD: 92033dbf14aSCameron Grant break; 92133dbf14aSCameron Grant case MOD_UNLOAD: 922285648f9SCameron Grant if (do_status(3, NULL)) 923c9b53085SCameron Grant return EBUSY; 92433dbf14aSCameron Grant if (status_dev) 92533dbf14aSCameron Grant destroy_dev(status_dev); 92633dbf14aSCameron Grant status_dev = 0; 92733dbf14aSCameron Grant break; 92833dbf14aSCameron Grant default: 92933dbf14aSCameron Grant break; 93033dbf14aSCameron Grant } 93133dbf14aSCameron Grant return 0; 93233dbf14aSCameron Grant } 93333dbf14aSCameron Grant 93433dbf14aSCameron Grant static moduledata_t sndpcm_mod = { 93533dbf14aSCameron Grant "snd_pcm", 93633dbf14aSCameron Grant sndpcm_modevent, 93733dbf14aSCameron Grant NULL 93833dbf14aSCameron Grant }; 93933dbf14aSCameron Grant DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 94033dbf14aSCameron Grant MODULE_VERSION(snd_pcm, PCM_MODVER); 941