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 38b8f0d9e0SCameron Grant #define PCM_MAXCHANS 256 39987e5972SCameron Grant 4033dbf14aSCameron Grant static dev_t status_dev = 0; 41285648f9SCameron Grant static int do_status(int action, struct uio *buf); 42987e5972SCameron Grant 43987e5972SCameron Grant static d_open_t sndopen; 44987e5972SCameron Grant static d_close_t sndclose; 45987e5972SCameron Grant static d_ioctl_t sndioctl; 46987e5972SCameron Grant static d_read_t sndread; 47987e5972SCameron Grant static d_write_t sndwrite; 48987e5972SCameron Grant static d_mmap_t sndmmap; 49987e5972SCameron Grant static d_poll_t sndpoll; 50987e5972SCameron Grant 51987e5972SCameron Grant #define CDEV_MAJOR 30 52987e5972SCameron Grant static struct cdevsw snd_cdevsw = { 53987e5972SCameron Grant /* open */ sndopen, 54987e5972SCameron Grant /* close */ sndclose, 55987e5972SCameron Grant /* read */ sndread, 56987e5972SCameron Grant /* write */ sndwrite, 57987e5972SCameron Grant /* ioctl */ sndioctl, 58987e5972SCameron Grant /* poll */ sndpoll, 59987e5972SCameron Grant /* mmap */ sndmmap, 60987e5972SCameron Grant /* strategy */ nostrategy, 61987e5972SCameron Grant /* name */ "snd", 62987e5972SCameron Grant /* maj */ CDEV_MAJOR, 63987e5972SCameron Grant /* dump */ nodump, 64987e5972SCameron Grant /* psize */ nopsize, 6566ef8af5SCameron Grant /* flags */ D_TRACKCLOSE, 66987e5972SCameron Grant }; 67987e5972SCameron Grant 68dd186369SCameron Grant /* 69dd186369SCameron Grant PROPOSAL: 70987e5972SCameron Grant each unit needs: 71987e5972SCameron Grant status, mixer, dsp, dspW, audio, sequencer, midi-in, seq2, sndproc = 9 devices 72987e5972SCameron Grant dspW and audio are deprecated. 73987e5972SCameron Grant dsp needs min 64 channels, will give it 256 74987e5972SCameron Grant 75dd186369SCameron Grant minor = (unit << 20) + (dev << 16) + channel 76dd186369SCameron Grant currently minor = (channel << 16) + (unit << 4) + dev 77987e5972SCameron Grant 78987e5972SCameron Grant nomenclature: 79987e5972SCameron Grant /dev/pcmX/dsp.(0..255) 80987e5972SCameron Grant /dev/pcmX/dspW 81987e5972SCameron Grant /dev/pcmX/audio 82987e5972SCameron Grant /dev/pcmX/status 83987e5972SCameron Grant /dev/pcmX/mixer 84987e5972SCameron Grant [etc.] 85987e5972SCameron Grant */ 86987e5972SCameron Grant 87987e5972SCameron Grant #define PCMMINOR(x) (minor(x)) 88a618cffeSCameron Grant #define PCMCHAN(x) ((PCMMINOR(x) & 0x00ff0000) >> 16) 89987e5972SCameron Grant #define PCMUNIT(x) ((PCMMINOR(x) & 0x000000f0) >> 4) 90987e5972SCameron Grant #define PCMDEV(x) (PCMMINOR(x) & 0x0000000f) 91dd186369SCameron Grant #define PCMMKMINOR(u, d, c) ((((c) & 0xff) << 16) | (((u) & 0x0f) << 4) | ((d) & 0x0f)) 92987e5972SCameron Grant 93987e5972SCameron Grant static devclass_t pcm_devclass; 9482db23e2SCameron Grant 9582db23e2SCameron Grant #ifdef USING_DEVFS 9681930014SPeter Wemm static int snd_unit = 0; 9709786698SPeter Wemm TUNABLE_INT("hw.snd.unit", &snd_unit); 9882db23e2SCameron Grant #endif 99987e5972SCameron Grant 10082db23e2SCameron Grant SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver"); 10182db23e2SCameron Grant 10237209180SCameron Grant void * 10337209180SCameron Grant snd_mtxcreate(const char *desc) 10437209180SCameron Grant { 10537209180SCameron Grant #ifdef USING_MUTEX 10637209180SCameron Grant struct mtx *m; 10737209180SCameron Grant 10837209180SCameron Grant m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); 10937209180SCameron Grant if (m == NULL) 11037209180SCameron Grant return NULL; 11137209180SCameron Grant mtx_init(m, desc, MTX_RECURSE); 11237209180SCameron Grant return m; 11337209180SCameron Grant #else 114a983d575SCameron Grant return (void *)0xcafebabe; 11537209180SCameron Grant #endif 11637209180SCameron Grant } 11737209180SCameron Grant 11837209180SCameron Grant void 11937209180SCameron Grant snd_mtxfree(void *m) 12037209180SCameron Grant { 12137209180SCameron Grant #ifdef USING_MUTEX 12237209180SCameron Grant struct mtx *mtx = m; 12337209180SCameron Grant 12437209180SCameron Grant mtx_assert(mtx, MA_OWNED); 12537209180SCameron Grant mtx_destroy(mtx); 12637209180SCameron Grant free(mtx, M_DEVBUF); 12737209180SCameron Grant #endif 12837209180SCameron Grant } 12937209180SCameron Grant 13037209180SCameron Grant void 13137209180SCameron Grant snd_mtxassert(void *m) 13237209180SCameron Grant { 13337209180SCameron Grant #ifdef USING_MUTEX 13437209180SCameron Grant struct mtx *mtx = m; 13537209180SCameron Grant 13637209180SCameron Grant mtx_assert(mtx, MA_OWNED); 13737209180SCameron Grant #endif 13837209180SCameron Grant } 13937209180SCameron Grant 14037209180SCameron Grant void 14137209180SCameron Grant snd_mtxlock(void *m) 14237209180SCameron Grant { 14337209180SCameron Grant #ifdef USING_MUTEX 14437209180SCameron Grant struct mtx *mtx = m; 14537209180SCameron Grant 14637209180SCameron Grant mtx_lock(mtx); 14737209180SCameron Grant #endif 14837209180SCameron Grant } 14937209180SCameron Grant 15037209180SCameron Grant void 15137209180SCameron Grant snd_mtxunlock(void *m) 15237209180SCameron Grant { 15337209180SCameron Grant #ifdef USING_MUTEX 15437209180SCameron Grant struct mtx *mtx = m; 15537209180SCameron Grant 15637209180SCameron Grant mtx_unlock(mtx); 15737209180SCameron Grant #endif 15837209180SCameron Grant } 15937209180SCameron Grant 16037209180SCameron Grant int 16137209180SCameron Grant snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) 16237209180SCameron Grant { 16337209180SCameron Grant #ifdef USING_MUTEX 16437209180SCameron Grant flags &= INTR_MPSAFE; 16537209180SCameron Grant flags |= INTR_TYPE_TTY; 16637209180SCameron Grant #else 16737209180SCameron Grant flags = INTR_TYPE_TTY; 16837209180SCameron Grant #endif 16937209180SCameron Grant return bus_setup_intr(dev, res, flags, hand, param, cookiep); 17037209180SCameron Grant } 17137209180SCameron Grant 172b8f0d9e0SCameron Grant /* return a locked channel */ 173285648f9SCameron Grant struct pcm_channel * 174b8f0d9e0SCameron Grant pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid) 175285648f9SCameron Grant { 176285648f9SCameron Grant struct pcm_channel *c; 177285648f9SCameron Grant struct snddev_channel *sce; 178285648f9SCameron Grant 179b8f0d9e0SCameron Grant snd_mtxassert(d->lock); 180285648f9SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 181285648f9SCameron Grant c = sce->channel; 18249c5e6e2SCameron Grant CHN_LOCK(c); 183285648f9SCameron Grant if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) { 184285648f9SCameron Grant c->flags |= CHN_F_BUSY; 185b8f0d9e0SCameron Grant c->pid = pid; 186285648f9SCameron Grant return c; 187285648f9SCameron Grant } 18849c5e6e2SCameron Grant CHN_UNLOCK(c); 189285648f9SCameron Grant } 190285648f9SCameron Grant return NULL; 191285648f9SCameron Grant } 192285648f9SCameron Grant 193b8f0d9e0SCameron Grant /* release a locked channel and unlock it */ 194285648f9SCameron Grant int 195b8f0d9e0SCameron Grant pcm_chnrelease(struct pcm_channel *c) 196285648f9SCameron Grant { 197b8f0d9e0SCameron Grant CHN_LOCKASSERT(c); 198285648f9SCameron Grant c->flags &= ~CHN_F_BUSY; 199b8f0d9e0SCameron Grant c->pid = -1; 20049c5e6e2SCameron Grant CHN_UNLOCK(c); 201285648f9SCameron Grant return 0; 202285648f9SCameron Grant } 203285648f9SCameron Grant 204285648f9SCameron Grant int 205285648f9SCameron Grant pcm_chnref(struct pcm_channel *c, int ref) 206285648f9SCameron Grant { 20749c5e6e2SCameron Grant int r; 20849c5e6e2SCameron Grant 209b8f0d9e0SCameron Grant CHN_LOCKASSERT(c); 210285648f9SCameron Grant c->refcount += ref; 21149c5e6e2SCameron Grant r = c->refcount; 21249c5e6e2SCameron Grant return r; 213285648f9SCameron Grant } 214285648f9SCameron Grant 21582db23e2SCameron Grant #ifdef USING_DEVFS 21633dbf14aSCameron Grant static void 217b8f0d9e0SCameron Grant snd_setdefaultunit(struct snddev_info *d) 218987e5972SCameron Grant { 21933dbf14aSCameron Grant static dev_t dsp = 0, dspW = 0, audio = 0, mixer = 0; 22033dbf14aSCameron Grant 22133dbf14aSCameron Grant if (dsp) { 22233dbf14aSCameron Grant destroy_dev(dsp); 22333dbf14aSCameron Grant dsp = 0; 224987e5972SCameron Grant } 22533dbf14aSCameron Grant if (dspW) { 22633dbf14aSCameron Grant destroy_dev(dspW); 22733dbf14aSCameron Grant dspW = 0; 22833dbf14aSCameron Grant } 22933dbf14aSCameron Grant if (audio) { 23033dbf14aSCameron Grant destroy_dev(audio); 23133dbf14aSCameron Grant audio = 0; 23233dbf14aSCameron Grant } 23333dbf14aSCameron Grant if (mixer) { 23433dbf14aSCameron Grant destroy_dev(mixer); 23533dbf14aSCameron Grant mixer = 0; 23633dbf14aSCameron Grant } 23733dbf14aSCameron Grant 238b8f0d9e0SCameron Grant if (d == NULL) 23933dbf14aSCameron Grant return; 24033dbf14aSCameron Grant 241b8f0d9e0SCameron Grant if (d->dspdev) 242b8f0d9e0SCameron Grant dsp = make_dev_alias(d->dspdev, "dsp"); 243b8f0d9e0SCameron Grant if (d->dspWdev) 244b8f0d9e0SCameron Grant dspW = make_dev_alias(d->dspWdev, "dspW"); 245b8f0d9e0SCameron Grant if (d->audiodev) 246b8f0d9e0SCameron Grant audio = make_dev_alias(d->audiodev, "audio"); 247b8f0d9e0SCameron Grant if (d->mixerdev) 248b8f0d9e0SCameron Grant mixer = make_dev_alias(d->mixerdev, "mixer"); 249b8f0d9e0SCameron Grant } 250b8f0d9e0SCameron Grant #endif 251b8f0d9e0SCameron Grant 252b8f0d9e0SCameron Grant static void 253b8f0d9e0SCameron Grant pcm_relinkdspunit(struct snddev_info *d) 254b8f0d9e0SCameron Grant { 255b8f0d9e0SCameron Grant #ifdef USING_DEVFS 256b8f0d9e0SCameron Grant int unit = device_get_unit(d->dev); 257b8f0d9e0SCameron Grant dev_t pdev; 258b8f0d9e0SCameron Grant 259b8f0d9e0SCameron Grant if (d->dspdev) { 260b8f0d9e0SCameron Grant destroy_dev(d->dspdev); 261b8f0d9e0SCameron Grant d->dspdev = 0; 262b8f0d9e0SCameron Grant } 263b8f0d9e0SCameron Grant if (d->dspWdev) { 264b8f0d9e0SCameron Grant destroy_dev(d->dspWdev); 265b8f0d9e0SCameron Grant d->dspWdev = 0; 266b8f0d9e0SCameron Grant } 267b8f0d9e0SCameron Grant if (d->audiodev) { 268b8f0d9e0SCameron Grant destroy_dev(d->audiodev); 269b8f0d9e0SCameron Grant d->audiodev = 0; 27033dbf14aSCameron Grant } 27133dbf14aSCameron Grant 272b8f0d9e0SCameron Grant if (d->defaultchan < d->chancount) { 273b8f0d9e0SCameron Grant pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, d->defaultchan)); 274b8f0d9e0SCameron Grant d->dspdev = make_dev_alias(pdev, "dsp%d", unit); 275b8f0d9e0SCameron Grant 276b8f0d9e0SCameron Grant pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, d->defaultchan)); 277b8f0d9e0SCameron Grant d->dspWdev = make_dev_alias(pdev, "dspW%d", unit); 278b8f0d9e0SCameron Grant 279b8f0d9e0SCameron Grant pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, d->defaultchan)); 280b8f0d9e0SCameron Grant d->audiodev = make_dev_alias(pdev, "audio%d", unit); 281b8f0d9e0SCameron Grant } 282b8f0d9e0SCameron Grant 283b8f0d9e0SCameron Grant if (unit == snd_unit) 284b8f0d9e0SCameron Grant snd_setdefaultunit(d); 285b8f0d9e0SCameron Grant #endif 286b8f0d9e0SCameron Grant } 287b8f0d9e0SCameron Grant 288b8f0d9e0SCameron Grant #ifdef USING_DEVFS 28933dbf14aSCameron Grant static int 29033dbf14aSCameron Grant sysctl_hw_sndunit(SYSCTL_HANDLER_ARGS) 29133dbf14aSCameron Grant { 292b8f0d9e0SCameron Grant struct snddev_info *d; 29333dbf14aSCameron Grant int error, unit; 29433dbf14aSCameron Grant 29533dbf14aSCameron Grant unit = snd_unit; 29633dbf14aSCameron Grant error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); 29733dbf14aSCameron Grant if (error == 0 && req->newptr != NULL) { 298b8f0d9e0SCameron Grant if (unit < 0 || unit > devclass_get_maxunit(pcm_devclass)) 299b8f0d9e0SCameron Grant return EINVAL; 300b8f0d9e0SCameron Grant d = devclass_get_softc(pcm_devclass, unit); 301b8f0d9e0SCameron Grant if (d == NULL || d->chancount == 0) 302b8f0d9e0SCameron Grant return EINVAL; 30333dbf14aSCameron Grant snd_unit = unit; 304b8f0d9e0SCameron Grant snd_setdefaultunit(d); 30533dbf14aSCameron Grant } 30633dbf14aSCameron Grant return (error); 30733dbf14aSCameron Grant } 308b3b7ccfeSJohn Baldwin SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW, 30933dbf14aSCameron Grant 0, sizeof(int), sysctl_hw_sndunit, "I", ""); 31082db23e2SCameron Grant #endif 311987e5972SCameron Grant 312285648f9SCameron Grant struct pcm_channel * 313285648f9SCameron Grant pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo) 314987e5972SCameron Grant { 315285648f9SCameron Grant struct pcm_channel *ch; 31633dbf14aSCameron Grant char *dirs; 3170f55ac6cSCameron Grant int err; 318987e5972SCameron Grant 319285648f9SCameron Grant switch(dir) { 320285648f9SCameron Grant case PCMDIR_PLAY: 321285648f9SCameron Grant dirs = "play"; 322285648f9SCameron Grant break; 323285648f9SCameron Grant case PCMDIR_REC: 324285648f9SCameron Grant dirs = "record"; 325285648f9SCameron Grant break; 326285648f9SCameron Grant case PCMDIR_VIRTUAL: 327285648f9SCameron Grant dirs = "virtual"; 328285648f9SCameron Grant dir = PCMDIR_PLAY; 329285648f9SCameron Grant break; 330285648f9SCameron Grant default: 331285648f9SCameron Grant return NULL; 3329c326820SCameron Grant } 333285648f9SCameron Grant 334285648f9SCameron Grant ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 335285648f9SCameron Grant if (!ch) 336285648f9SCameron Grant return NULL; 337285648f9SCameron Grant 3380f55ac6cSCameron Grant ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK); 339285648f9SCameron Grant if (!ch->methods) { 340285648f9SCameron Grant free(ch, M_DEVBUF); 341285648f9SCameron Grant return NULL; 342285648f9SCameron Grant } 343285648f9SCameron Grant 344285648f9SCameron Grant ch->pid = -1; 345285648f9SCameron Grant ch->parentsnddev = d; 346285648f9SCameron Grant ch->parentchannel = parent; 347285648f9SCameron Grant snprintf(ch->name, 32, "%s:%d:%s", device_get_nameunit(d->dev), d->chancount, dirs); 348285648f9SCameron Grant 3490f55ac6cSCameron Grant err = chn_init(ch, devinfo, dir); 3500f55ac6cSCameron Grant if (err) { 351285648f9SCameron Grant device_printf(d->dev, "chn_init() for channel %d (%s) failed: err = %d\n", d->chancount, dirs, err); 352285648f9SCameron Grant kobj_delete(ch->methods, M_DEVBUF); 353285648f9SCameron Grant free(ch, M_DEVBUF); 354285648f9SCameron Grant return NULL; 355bbb5bf3dSCameron Grant } 356285648f9SCameron Grant 357285648f9SCameron Grant return ch; 358285648f9SCameron Grant } 359285648f9SCameron Grant 360285648f9SCameron Grant int 361285648f9SCameron Grant pcm_chn_destroy(struct pcm_channel *ch) 362285648f9SCameron Grant { 363285648f9SCameron Grant int err; 364285648f9SCameron Grant 365285648f9SCameron Grant err = chn_kill(ch); 366285648f9SCameron Grant if (err) { 367285648f9SCameron Grant device_printf(ch->parentsnddev->dev, "chn_kill() for %s failed, err = %d\n", ch->name, err); 368285648f9SCameron Grant return err; 369285648f9SCameron Grant } 370285648f9SCameron Grant 371285648f9SCameron Grant kobj_delete(ch->methods, M_DEVBUF); 372285648f9SCameron Grant free(ch, M_DEVBUF); 373285648f9SCameron Grant 374285648f9SCameron Grant return 0; 375285648f9SCameron Grant } 376285648f9SCameron Grant 377285648f9SCameron Grant int 378285648f9SCameron Grant pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) 379285648f9SCameron Grant { 380285648f9SCameron Grant struct snddev_channel *sce; 381b8f0d9e0SCameron Grant struct pcm_channel **aplay, **arec; 382285648f9SCameron Grant int unit = device_get_unit(d->dev); 383b8f0d9e0SCameron Grant int cc, sz; 384b8f0d9e0SCameron Grant 385b8f0d9e0SCameron Grant snd_mtxlock(d->lock); 386b8f0d9e0SCameron Grant if (d->chancount == d->maxchans) { 387b8f0d9e0SCameron Grant cc = d->maxchans? d->maxchans * 2 : 2; 388b8f0d9e0SCameron Grant sz = cc * sizeof(struct pcm_channel *); 389b8f0d9e0SCameron Grant aplay = malloc(sz, M_DEVBUF, M_WAITOK | M_ZERO); 390b8f0d9e0SCameron Grant arec = malloc(sz, M_DEVBUF, M_WAITOK | M_ZERO); 391b8f0d9e0SCameron Grant if (aplay == NULL || arec == NULL) { 392b8f0d9e0SCameron Grant if (aplay) 393b8f0d9e0SCameron Grant free(aplay, M_DEVBUF); 394b8f0d9e0SCameron Grant if (arec) 395b8f0d9e0SCameron Grant free(arec, M_DEVBUF); 396b8f0d9e0SCameron Grant snd_mtxunlock(d->lock); 397b8f0d9e0SCameron Grant return EINVAL; 398b8f0d9e0SCameron Grant } 399b8f0d9e0SCameron Grant if (d->aplay) { 400b8f0d9e0SCameron Grant bcopy(d->aplay, aplay, d->maxchans * sizeof(struct pcm_channel *)); 401b8f0d9e0SCameron Grant free(d->aplay, M_DEVBUF); 402b8f0d9e0SCameron Grant } 403b8f0d9e0SCameron Grant d->aplay = aplay; 404b8f0d9e0SCameron Grant if (d->arec) { 405b8f0d9e0SCameron Grant bcopy(d->arec, arec, d->maxchans * sizeof(struct pcm_channel *)); 406b8f0d9e0SCameron Grant free(d->arec, M_DEVBUF); 407b8f0d9e0SCameron Grant } 408b8f0d9e0SCameron Grant d->arec = arec; 409b8f0d9e0SCameron Grant d->maxchans = cc; 410b8f0d9e0SCameron Grant } 411285648f9SCameron Grant 412285648f9SCameron Grant sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO); 413285648f9SCameron Grant if (!sce) { 414b8f0d9e0SCameron Grant snd_mtxunlock(d->lock); 415285648f9SCameron Grant return ENOMEM; 416285648f9SCameron Grant } 417285648f9SCameron Grant 418285648f9SCameron Grant sce->channel = ch; 419285648f9SCameron Grant SLIST_INSERT_HEAD(&d->channels, sce, link); 420285648f9SCameron Grant 4217207eca6SCameron Grant make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount), 4227207eca6SCameron Grant UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", unit, d->chancount); 4237207eca6SCameron Grant make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount), 4247207eca6SCameron Grant UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", unit, d->chancount); 42533dbf14aSCameron Grant make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount), 42633dbf14aSCameron Grant UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", unit, d->chancount); 4277207eca6SCameron Grant /* XXX SND_DEV_NORESET? */ 428285648f9SCameron Grant 429285648f9SCameron Grant if (d->chancount++ == 0) 430b8f0d9e0SCameron Grant pcm_relinkdspunit(d); 431b8f0d9e0SCameron Grant 43249c5e6e2SCameron Grant snd_mtxunlock(d->lock); 433285648f9SCameron Grant 43433dbf14aSCameron Grant return 0; 43533dbf14aSCameron Grant } 43633dbf14aSCameron Grant 437285648f9SCameron Grant int 438285648f9SCameron Grant pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch) 43933dbf14aSCameron Grant { 440285648f9SCameron Grant struct snddev_channel *sce; 441285648f9SCameron Grant int unit = device_get_unit(d->dev); 44233dbf14aSCameron Grant dev_t pdev; 44333dbf14aSCameron Grant 44449c5e6e2SCameron Grant snd_mtxlock(d->lock); 445285648f9SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 446285648f9SCameron Grant if (sce->channel == ch) 447285648f9SCameron Grant goto gotit; 44833dbf14aSCameron Grant } 44949c5e6e2SCameron Grant snd_mtxunlock(d->lock); 450285648f9SCameron Grant return EINVAL; 451285648f9SCameron Grant gotit: 45233dbf14aSCameron Grant d->chancount--; 453285648f9SCameron Grant SLIST_REMOVE(&d->channels, sce, snddev_channel, link); 454285648f9SCameron Grant free(sce, M_DEVBUF); 455285648f9SCameron Grant 456285648f9SCameron Grant if (d->chancount == 0) 457b8f0d9e0SCameron Grant pcm_relinkdspunit(d); 45833dbf14aSCameron Grant pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount)); 45933dbf14aSCameron Grant destroy_dev(pdev); 46033dbf14aSCameron Grant pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount)); 46133dbf14aSCameron Grant destroy_dev(pdev); 46233dbf14aSCameron Grant pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount)); 46333dbf14aSCameron Grant destroy_dev(pdev); 46449c5e6e2SCameron Grant snd_mtxunlock(d->lock); 465285648f9SCameron Grant 466987e5972SCameron Grant return 0; 467987e5972SCameron Grant } 468987e5972SCameron Grant 469987e5972SCameron Grant int 470285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 471285648f9SCameron Grant { 472285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 473285648f9SCameron Grant struct pcm_channel *ch; 474285648f9SCameron Grant int err; 475285648f9SCameron Grant 476285648f9SCameron Grant ch = pcm_chn_create(d, NULL, cls, dir, devinfo); 477285648f9SCameron Grant if (!ch) { 478285648f9SCameron Grant device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo); 479285648f9SCameron Grant return ENODEV; 480285648f9SCameron Grant } 481285648f9SCameron Grant err = pcm_chn_add(d, ch); 482285648f9SCameron Grant if (err) { 483285648f9SCameron Grant device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err); 484285648f9SCameron Grant pcm_chn_destroy(ch); 485285648f9SCameron Grant } 486285648f9SCameron Grant 487285648f9SCameron Grant return err; 488285648f9SCameron Grant } 489285648f9SCameron Grant 490285648f9SCameron Grant static int 491285648f9SCameron Grant pcm_killchan(device_t dev) 492285648f9SCameron Grant { 493285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 494285648f9SCameron Grant struct snddev_channel *sce; 495285648f9SCameron Grant 49649c5e6e2SCameron Grant snd_mtxlock(d->lock); 497285648f9SCameron Grant sce = SLIST_FIRST(&d->channels); 49849c5e6e2SCameron Grant snd_mtxunlock(d->lock); 499285648f9SCameron Grant 500285648f9SCameron Grant return pcm_chn_remove(d, sce->channel); 501285648f9SCameron Grant } 502285648f9SCameron Grant 503285648f9SCameron Grant int 504987e5972SCameron Grant pcm_setstatus(device_t dev, char *str) 505987e5972SCameron Grant { 50666ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 50749c5e6e2SCameron Grant 50849c5e6e2SCameron Grant snd_mtxlock(d->lock); 509987e5972SCameron Grant strncpy(d->status, str, SND_STATUSLEN); 51049c5e6e2SCameron Grant snd_mtxunlock(d->lock); 511987e5972SCameron Grant return 0; 512987e5972SCameron Grant } 513987e5972SCameron Grant 514987e5972SCameron Grant u_int32_t 515987e5972SCameron Grant pcm_getflags(device_t dev) 516987e5972SCameron Grant { 51766ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 51849c5e6e2SCameron Grant 519987e5972SCameron Grant return d->flags; 520987e5972SCameron Grant } 521987e5972SCameron Grant 522987e5972SCameron Grant void 523987e5972SCameron Grant pcm_setflags(device_t dev, u_int32_t val) 524987e5972SCameron Grant { 52566ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 526b8f0d9e0SCameron Grant /* 527b8f0d9e0SCameron Grant if ((val & SD_F_SIMPLEX) && (d->fakechan == NULL)) { 528b8f0d9e0SCameron Grant device_printf(dev, "set simplex mode\n"); 529b8f0d9e0SCameron Grant d->fakechan = fkchan_setup(dev); 530b8f0d9e0SCameron Grant chn_init(d->fakechan, NULL, 0); 531b8f0d9e0SCameron Grant } 532b8f0d9e0SCameron Grant */ 533987e5972SCameron Grant d->flags = val; 534987e5972SCameron Grant } 535987e5972SCameron Grant 53639004e69SCameron Grant void * 53739004e69SCameron Grant pcm_getdevinfo(device_t dev) 53839004e69SCameron Grant { 53966ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 54049c5e6e2SCameron Grant 54139004e69SCameron Grant return d->devinfo; 54239004e69SCameron Grant } 54339004e69SCameron Grant 544987e5972SCameron Grant /* This is the generic init routine */ 545987e5972SCameron Grant int 546987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 547987e5972SCameron Grant { 548b8f0d9e0SCameron Grant int unit = device_get_unit(dev); 54966ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 550987e5972SCameron Grant 55149c5e6e2SCameron Grant d->lock = snd_mtxcreate(device_get_nameunit(dev)); 55249c5e6e2SCameron Grant snd_mtxlock(d->lock); 553987e5972SCameron Grant if (!pcm_devclass) { 554987e5972SCameron Grant pcm_devclass = device_get_devclass(dev); 55533dbf14aSCameron Grant status_dev = make_dev(&snd_cdevsw, PCMMKMINOR(0, SND_DEV_STATUS, 0), 556083279e4SSeigo Tanimura UID_ROOT, GID_WHEEL, 0444, "sndstat"); 557083279e4SSeigo Tanimura } 558285648f9SCameron Grant 559b8f0d9e0SCameron Grant d->mixerdev = make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0), 56011346231SSeigo Tanimura UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit); 561285648f9SCameron Grant 562b8f0d9e0SCameron Grant d->dspdev = 0; 563b8f0d9e0SCameron Grant d->dspWdev = 0; 564b8f0d9e0SCameron Grant d->audiodev = 0; 565e4d5b250SCameron Grant d->dev = dev; 566987e5972SCameron Grant d->devinfo = devinfo; 567285648f9SCameron Grant d->chancount = 0; 568b8f0d9e0SCameron Grant d->defaultchan = 0; 569b8f0d9e0SCameron Grant d->maxchans = 0; 570b8f0d9e0SCameron Grant d->aplay = NULL; 571b8f0d9e0SCameron Grant d->arec = NULL; 572b8f0d9e0SCameron Grant /* 573285648f9SCameron Grant sz = d->maxchans * sizeof(struct pcm_channel *); 574833f7023SCameron Grant 575833f7023SCameron Grant if (sz > 0) { 576285648f9SCameron Grant d->aplay = (struct pcm_channel **)malloc(sz, M_DEVBUF, M_WAITOK | M_ZERO); 577285648f9SCameron Grant d->arec = (struct pcm_channel **)malloc(sz, M_DEVBUF, M_WAITOK | M_ZERO); 578285648f9SCameron Grant if (!d->arec || !d->aplay) goto no; 579b8f0d9e0SCameron Grant } 580b8f0d9e0SCameron Grant */ 581b8f0d9e0SCameron Grant if (((numplay == 0) || (numrec == 0)) && (numplay != numrec)) { 582285648f9SCameron Grant d->flags |= SD_F_SIMPLEX; 583b8f0d9e0SCameron Grant } 584285648f9SCameron Grant d->fakechan = fkchan_setup(dev); 585285648f9SCameron Grant chn_init(d->fakechan, NULL, 0); 586987e5972SCameron Grant 58782db23e2SCameron Grant #ifdef SND_DYNSYSCTL 588cc486d80SJohn Baldwin sysctl_ctx_init(&d->sysctl_tree); 589cc486d80SJohn Baldwin d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree, 590a3e893e0SJohn Baldwin SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO, 591cc486d80SJohn Baldwin device_get_nameunit(dev), CTLFLAG_RD, 0, ""); 592a3e893e0SJohn Baldwin if (d->sysctl_tree_top == NULL) { 593cc486d80SJohn Baldwin sysctl_ctx_free(&d->sysctl_tree); 594cc486d80SJohn Baldwin goto no; 595cc486d80SJohn Baldwin } 59682db23e2SCameron Grant #endif 59749c5e6e2SCameron Grant #if 1 598b8f0d9e0SCameron Grant if (numplay > 0) 599285648f9SCameron Grant vchan_initsys(d); 600285648f9SCameron Grant #endif 601b8f0d9e0SCameron Grant snd_setdefaultunit(d); 60249c5e6e2SCameron Grant snd_mtxunlock(d->lock); 603987e5972SCameron Grant return 0; 604987e5972SCameron Grant no: 605b8f0d9e0SCameron Grant /* 606987e5972SCameron Grant if (d->aplay) free(d->aplay, M_DEVBUF); 607987e5972SCameron Grant if (d->arec) free(d->arec, M_DEVBUF); 608b8f0d9e0SCameron Grant */ 60949c5e6e2SCameron Grant /* snd_mtxunlock(d->lock); */ 61049c5e6e2SCameron Grant snd_mtxfree(d->lock); 611987e5972SCameron Grant return ENXIO; 612987e5972SCameron Grant } 613987e5972SCameron Grant 61433dbf14aSCameron Grant int 61533dbf14aSCameron Grant pcm_unregister(device_t dev) 6167c438dbeSCameron Grant { 617285648f9SCameron Grant int unit = device_get_unit(dev); 61866ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 619285648f9SCameron Grant struct snddev_channel *sce; 6207c438dbeSCameron Grant 62149c5e6e2SCameron Grant snd_mtxlock(d->lock); 622285648f9SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 623285648f9SCameron Grant if (sce->channel->refcount > 0) { 624c9b53085SCameron Grant device_printf(dev, "unregister: channel busy"); 62549c5e6e2SCameron Grant snd_mtxunlock(d->lock); 626285648f9SCameron Grant return EBUSY; 627285648f9SCameron Grant } 628c9b53085SCameron Grant } 6290f55ac6cSCameron Grant if (mixer_isbusy(d->mixer)) { 630c9b53085SCameron Grant device_printf(dev, "unregister: mixer busy"); 63149c5e6e2SCameron Grant snd_mtxunlock(d->lock); 632c9b53085SCameron Grant return EBUSY; 633c9b53085SCameron Grant } 6347c438dbeSCameron Grant 63566ef8af5SCameron Grant #ifdef SND_DYNSYSCTL 63666ef8af5SCameron Grant d->sysctl_tree_top = NULL; 63766ef8af5SCameron Grant sysctl_ctx_free(&d->sysctl_tree); 63866ef8af5SCameron Grant #endif 639b8f0d9e0SCameron Grant if (unit == snd_unit) 640b8f0d9e0SCameron Grant snd_setdefaultunit(NULL); 641b8f0d9e0SCameron Grant destroy_dev(d->mixerdev); 64233dbf14aSCameron Grant mixer_uninit(dev); 6437c438dbeSCameron Grant 644285648f9SCameron Grant while (d->chancount > 0) 645285648f9SCameron Grant pcm_killchan(dev); 6467c438dbeSCameron Grant 64733dbf14aSCameron Grant if (d->aplay) free(d->aplay, M_DEVBUF); 64833dbf14aSCameron Grant if (d->arec) free(d->arec, M_DEVBUF); 64933dbf14aSCameron Grant 65066ef8af5SCameron Grant chn_kill(d->fakechan); 65166ef8af5SCameron Grant fkchan_kill(d->fakechan); 65282db23e2SCameron Grant 65349c5e6e2SCameron Grant /* snd_mtxunlock(d->lock); */ 65449c5e6e2SCameron Grant snd_mtxfree(d->lock); 65533dbf14aSCameron Grant return 0; 65633dbf14aSCameron Grant } 6577c438dbeSCameron Grant 658987e5972SCameron Grant /* 659987e5972SCameron Grant * a small utility function which, given a device number, returns 66066ef8af5SCameron Grant * a pointer to the associated struct snddev_info struct, and sets the unit 661987e5972SCameron Grant * number. 662987e5972SCameron Grant */ 66366ef8af5SCameron Grant static struct snddev_info * 664987e5972SCameron Grant get_snddev_info(dev_t i_dev, int *unit, int *dev, int *chan) 665987e5972SCameron Grant { 66666ef8af5SCameron Grant struct snddev_info *sc; 667987e5972SCameron Grant int u, d, c; 668987e5972SCameron Grant 669987e5972SCameron Grant u = PCMUNIT(i_dev); 670987e5972SCameron Grant d = PCMDEV(i_dev); 671987e5972SCameron Grant c = PCMCHAN(i_dev); 672987e5972SCameron Grant if (u > devclass_get_maxunit(pcm_devclass)) u = -1; 673987e5972SCameron Grant if (unit) *unit = u; 674987e5972SCameron Grant if (dev) *dev = d; 675987e5972SCameron Grant if (chan) *chan = c; 676987e5972SCameron Grant if (u < 0) return NULL; 677987e5972SCameron Grant 67833dbf14aSCameron Grant sc = devclass_get_softc(pcm_devclass, u); 67966ef8af5SCameron Grant if (sc == NULL) return NULL; 68033dbf14aSCameron Grant 681987e5972SCameron Grant switch(d) { 682987e5972SCameron Grant case SND_DEV_CTL: /* /dev/mixer handled by pcm */ 683987e5972SCameron Grant case SND_DEV_STATUS: /* /dev/sndstat handled by pcm */ 684987e5972SCameron Grant case SND_DEV_DSP: 685987e5972SCameron Grant case SND_DEV_DSP16: 686987e5972SCameron Grant case SND_DEV_AUDIO: 68733dbf14aSCameron Grant return sc; 688987e5972SCameron Grant 689987e5972SCameron Grant case SND_DEV_SEQ: /* XXX when enabled... */ 690987e5972SCameron Grant case SND_DEV_SEQ2: 691987e5972SCameron Grant case SND_DEV_MIDIN: 692987e5972SCameron Grant case SND_DEV_SNDPROC: /* /dev/sndproc handled by pcm */ 693987e5972SCameron Grant default: 694987e5972SCameron Grant printf("unsupported subdevice %d\n", d); 695987e5972SCameron Grant return NULL; 696987e5972SCameron Grant } 697987e5972SCameron Grant } 698987e5972SCameron Grant 699987e5972SCameron Grant static int 700987e5972SCameron Grant sndopen(dev_t i_dev, int flags, int mode, struct proc *p) 701987e5972SCameron Grant { 702987e5972SCameron Grant int dev, unit, chan; 70366ef8af5SCameron Grant struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 704987e5972SCameron Grant 705987e5972SCameron Grant DEB(printf("open snd%d subdev %d flags 0x%08x mode 0x%08x\n", 706987e5972SCameron Grant unit, dev, flags, mode)); 707987e5972SCameron Grant 708987e5972SCameron Grant switch(dev) { 709987e5972SCameron Grant case SND_DEV_STATUS: 710285648f9SCameron Grant return do_status(0, NULL); 711987e5972SCameron Grant 712987e5972SCameron Grant case SND_DEV_CTL: 7130f55ac6cSCameron Grant return d? mixer_busy(d->mixer, 1) : ENXIO; 714987e5972SCameron Grant 715987e5972SCameron Grant case SND_DEV_AUDIO: 716987e5972SCameron Grant case SND_DEV_DSP: 717987e5972SCameron Grant case SND_DEV_DSP16: 7185b78a734SCameron Grant case SND_DEV_NORESET: 719285648f9SCameron Grant return d? dsp_open(d, chan, flags, dev, p->p_pid) : ENXIO; 720987e5972SCameron Grant 721987e5972SCameron Grant default: 722987e5972SCameron Grant return ENXIO; 723987e5972SCameron Grant } 724987e5972SCameron Grant } 725987e5972SCameron Grant 726987e5972SCameron Grant static int 727987e5972SCameron Grant sndclose(dev_t i_dev, int flags, int mode, struct proc *p) 728987e5972SCameron Grant { 729987e5972SCameron Grant int dev, unit, chan; 73066ef8af5SCameron Grant struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 731987e5972SCameron Grant 732987e5972SCameron Grant DEB(printf("close snd%d subdev %d\n", unit, dev)); 733987e5972SCameron Grant 734987e5972SCameron Grant switch(dev) { /* only those for which close makes sense */ 735987e5972SCameron Grant case SND_DEV_STATUS: 736285648f9SCameron Grant return do_status(1, NULL); 737987e5972SCameron Grant 738987e5972SCameron Grant case SND_DEV_CTL: 7390f55ac6cSCameron Grant return d? mixer_busy(d->mixer, 0) : ENXIO; 740987e5972SCameron Grant 741987e5972SCameron Grant case SND_DEV_AUDIO: 742987e5972SCameron Grant case SND_DEV_DSP: 743987e5972SCameron Grant case SND_DEV_DSP16: 744987e5972SCameron Grant return d? dsp_close(d, chan, dev) : ENXIO; 745987e5972SCameron Grant 746987e5972SCameron Grant default: 747987e5972SCameron Grant return ENXIO; 748987e5972SCameron Grant } 749987e5972SCameron Grant } 750987e5972SCameron Grant 751987e5972SCameron Grant static int 752987e5972SCameron Grant sndread(dev_t i_dev, struct uio *buf, int flag) 753987e5972SCameron Grant { 754987e5972SCameron Grant int dev, unit, chan; 75566ef8af5SCameron Grant struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 756987e5972SCameron Grant DEB(printf("read snd%d subdev %d flag 0x%08x\n", unit, dev, flag)); 757987e5972SCameron Grant 758987e5972SCameron Grant switch(dev) { 759987e5972SCameron Grant case SND_DEV_STATUS: 760285648f9SCameron Grant return do_status(2, buf); 761987e5972SCameron Grant 762987e5972SCameron Grant case SND_DEV_AUDIO: 763987e5972SCameron Grant case SND_DEV_DSP: 764987e5972SCameron Grant case SND_DEV_DSP16: 765987e5972SCameron Grant return d? dsp_read(d, chan, buf, flag) : EBADF; 766987e5972SCameron Grant 767987e5972SCameron Grant default: 768987e5972SCameron Grant return ENXIO; 769987e5972SCameron Grant } 770987e5972SCameron Grant } 771987e5972SCameron Grant 772987e5972SCameron Grant static int 773987e5972SCameron Grant sndwrite(dev_t i_dev, struct uio *buf, int flag) 774987e5972SCameron Grant { 775987e5972SCameron Grant int dev, unit, chan; 77666ef8af5SCameron Grant struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 777987e5972SCameron Grant 778987e5972SCameron Grant DEB(printf("write snd%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag)); 779987e5972SCameron Grant 780987e5972SCameron Grant switch(dev) { /* only writeable devices */ 781987e5972SCameron Grant case SND_DEV_DSP: 782987e5972SCameron Grant case SND_DEV_DSP16: 783987e5972SCameron Grant case SND_DEV_AUDIO: 784987e5972SCameron Grant return d? dsp_write(d, chan, buf, flag) : EBADF; 785987e5972SCameron Grant 786987e5972SCameron Grant default: 787987e5972SCameron Grant return EPERM; /* for non-writeable devices ; */ 788987e5972SCameron Grant } 789987e5972SCameron Grant } 790987e5972SCameron Grant 791987e5972SCameron Grant static int 792987e5972SCameron Grant sndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p) 793987e5972SCameron Grant { 794987e5972SCameron Grant int dev, chan; 79566ef8af5SCameron Grant struct snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan); 796987e5972SCameron Grant 797987e5972SCameron Grant if (d == NULL) return ENXIO; 798987e5972SCameron Grant 799987e5972SCameron Grant switch(dev) { 800987e5972SCameron Grant case SND_DEV_CTL: 801987e5972SCameron Grant return mixer_ioctl(d, cmd, arg); 802987e5972SCameron Grant 803987e5972SCameron Grant case SND_DEV_AUDIO: 804987e5972SCameron Grant case SND_DEV_DSP: 805987e5972SCameron Grant case SND_DEV_DSP16: 8061ad869dbSCameron Grant if (IOCGROUP(cmd) == 'M') 8071ad869dbSCameron Grant return mixer_ioctl(d, cmd, arg); 8081ad869dbSCameron Grant else 809987e5972SCameron Grant return dsp_ioctl(d, chan, cmd, arg); 810987e5972SCameron Grant 811987e5972SCameron Grant default: 812987e5972SCameron Grant return ENXIO; 813987e5972SCameron Grant } 814987e5972SCameron Grant } 815987e5972SCameron Grant 816987e5972SCameron Grant static int 817987e5972SCameron Grant sndpoll(dev_t i_dev, int events, struct proc *p) 818987e5972SCameron Grant { 819987e5972SCameron Grant int dev, chan; 82066ef8af5SCameron Grant struct snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan); 821987e5972SCameron Grant 8220e25481fSCameron Grant DEB(printf("sndpoll d 0x%p dev 0x%04x events 0x%08x\n", d, dev, events)); 823987e5972SCameron Grant 824987e5972SCameron Grant if (d == NULL) return ENXIO; 825987e5972SCameron Grant 826987e5972SCameron Grant switch(dev) { 827987e5972SCameron Grant case SND_DEV_AUDIO: 828987e5972SCameron Grant case SND_DEV_DSP: 829987e5972SCameron Grant case SND_DEV_DSP16: 830987e5972SCameron Grant return dsp_poll(d, chan, events, p); 831987e5972SCameron Grant 832987e5972SCameron Grant default: 833987e5972SCameron Grant return (events & 834987e5972SCameron Grant (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)) | POLLHUP; 835987e5972SCameron Grant } 836987e5972SCameron Grant } 837987e5972SCameron Grant 838987e5972SCameron Grant /* 839987e5972SCameron Grant * The mmap interface allows access to the play and read buffer, 840987e5972SCameron Grant * plus the device descriptor. 841987e5972SCameron Grant * The various blocks are accessible at the following offsets: 842987e5972SCameron Grant * 843987e5972SCameron Grant * 0x00000000 ( 0 ) : write buffer ; 844987e5972SCameron Grant * 0x01000000 (16 MB) : read buffer ; 845987e5972SCameron Grant * 0x02000000 (32 MB) : device descriptor (dangerous!) 846987e5972SCameron Grant * 847987e5972SCameron Grant * WARNING: the mmap routines assume memory areas are aligned. This 848987e5972SCameron Grant * is true (probably) for the dma buffers, but likely false for the 849987e5972SCameron Grant * device descriptor. As a consequence, we do not know where it is 850987e5972SCameron Grant * located in the requested area. 851987e5972SCameron Grant */ 852987e5972SCameron Grant static int 853987e5972SCameron Grant sndmmap(dev_t i_dev, vm_offset_t offset, int nprot) 854987e5972SCameron Grant { 855987e5972SCameron Grant int unit, dev, chan; 85666ef8af5SCameron Grant struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 857987e5972SCameron Grant 858987e5972SCameron Grant DEB(printf("sndmmap d 0x%p dev 0x%04x ofs 0x%08x nprot 0x%08x\n", 859987e5972SCameron Grant d, dev, offset, nprot)); 860987e5972SCameron Grant 861987e5972SCameron Grant if (d == NULL || nprot & PROT_EXEC) return -1; /* forbidden */ 862987e5972SCameron Grant 863987e5972SCameron Grant switch(dev) { 864987e5972SCameron Grant case SND_DEV_AUDIO: 865987e5972SCameron Grant case SND_DEV_DSP: 866987e5972SCameron Grant case SND_DEV_DSP16: 867987e5972SCameron Grant return dsp_mmap(d, chan, offset, nprot); 868987e5972SCameron Grant 869987e5972SCameron Grant default: 870987e5972SCameron Grant return -1; 871987e5972SCameron Grant } 872987e5972SCameron Grant } 873987e5972SCameron Grant 874987e5972SCameron Grant static int 875285648f9SCameron Grant status_init(struct sbuf *s) 876987e5972SCameron Grant { 877285648f9SCameron Grant int i, pc, rc, vc; 878987e5972SCameron Grant device_t dev; 87966ef8af5SCameron Grant struct snddev_info *d; 880285648f9SCameron Grant struct snddev_channel *sce; 881285648f9SCameron Grant struct pcm_channel *c; 88249c5e6e2SCameron Grant #ifdef SNDSTAT_VERBOSE 883285648f9SCameron Grant struct pcm_feeder *f; 88449c5e6e2SCameron Grant #endif 885987e5972SCameron Grant 886285648f9SCameron Grant sbuf_printf(s, "FreeBSD Audio Driver (newpcm) %s %s\nInstalled devices:\n", 887285648f9SCameron Grant __DATE__, __TIME__); 888987e5972SCameron Grant 889987e5972SCameron Grant for (i = 0; i <= devclass_get_maxunit(pcm_devclass); i++) { 89033dbf14aSCameron Grant d = devclass_get_softc(pcm_devclass, i); 891285648f9SCameron Grant if (!d) 892285648f9SCameron Grant continue; 89349c5e6e2SCameron Grant snd_mtxlock(d->lock); 894987e5972SCameron Grant dev = devclass_get_device(pcm_devclass, i); 895285648f9SCameron Grant sbuf_printf(s, "pcm%d: <%s> %s", i, device_get_desc(dev), d->status); 896285648f9SCameron Grant if (d->chancount > 0) { 897285648f9SCameron Grant pc = rc = vc = 0; 898285648f9SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 899285648f9SCameron Grant c = sce->channel; 900285648f9SCameron Grant if (c->direction == PCMDIR_PLAY) { 901285648f9SCameron Grant if (c->flags & CHN_F_VIRTUAL) 902285648f9SCameron Grant vc++; 903bf8ca271SCameron Grant else 904285648f9SCameron Grant pc++; 905285648f9SCameron Grant } else 906285648f9SCameron Grant rc++; 907bf8ca271SCameron Grant } 908285648f9SCameron Grant sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)\n", pc, rc, vc, 909285648f9SCameron Grant (d->flags & SD_F_SIMPLEX)? "" : " duplex", 910285648f9SCameron Grant #ifdef USING_DEVFS 911285648f9SCameron Grant (i == snd_unit)? " default" : "" 912285648f9SCameron Grant #else 913285648f9SCameron Grant "" 914285648f9SCameron Grant #endif 915285648f9SCameron Grant ); 916285648f9SCameron Grant #ifdef SNDSTAT_VERBOSE 917285648f9SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 918285648f9SCameron Grant c = sce->channel; 919285648f9SCameron Grant sbuf_printf(s, "\t%s[%s]: speed %d, format %08x, flags %08x", 920285648f9SCameron Grant c->parentchannel? c->parentchannel->name : "", 921285648f9SCameron Grant c->name, c->speed, c->format, c->flags); 922285648f9SCameron Grant if (c->pid != -1) 923285648f9SCameron Grant sbuf_printf(s, ", pid %d", c->pid); 924285648f9SCameron Grant sbuf_printf(s, "\n\t"); 925285648f9SCameron Grant f = c->feeder; 926285648f9SCameron Grant while (f) { 927285648f9SCameron Grant sbuf_printf(s, "%s", f->class->name); 928285648f9SCameron Grant if (f->desc->type == FEEDER_FMT) 929285648f9SCameron Grant sbuf_printf(s, "(%08x <- %08x)", f->desc->out, f->desc->in); 930285648f9SCameron Grant if (f->desc->type == FEEDER_RATE) 931285648f9SCameron Grant sbuf_printf(s, "(%d <- %d)", FEEDER_GET(f, FEEDRATE_DST), FEEDER_GET(f, FEEDRATE_SRC)); 932285648f9SCameron Grant if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER) 933285648f9SCameron Grant sbuf_printf(s, "(%08x)", f->desc->out); 934285648f9SCameron Grant if (f->source) 935285648f9SCameron Grant sbuf_printf(s, " <- "); 936285648f9SCameron Grant f = f->source; 937987e5972SCameron Grant } 938285648f9SCameron Grant sbuf_printf(s, "\n"); 939285648f9SCameron Grant } 940285648f9SCameron Grant #endif 941285648f9SCameron Grant } else 942285648f9SCameron Grant sbuf_printf(s, " (mixer only)\n"); 94349c5e6e2SCameron Grant snd_mtxunlock(d->lock); 944285648f9SCameron Grant } 945285648f9SCameron Grant sbuf_finish(s); 946285648f9SCameron Grant return sbuf_len(s); 947987e5972SCameron Grant } 948987e5972SCameron Grant 949987e5972SCameron Grant static int 950285648f9SCameron Grant do_status(int action, struct uio *buf) 951987e5972SCameron Grant { 952285648f9SCameron Grant static struct sbuf s; 953285648f9SCameron Grant static int bufptr = 0; 954285648f9SCameron Grant static int status_open = 0; 955285648f9SCameron Grant int l, err; 956987e5972SCameron Grant 957285648f9SCameron Grant switch(action) { 958285648f9SCameron Grant case 0: /* open */ 959285648f9SCameron Grant if (status_open) 960285648f9SCameron Grant return EBUSY; 961d6479358SDag-Erling Smørgrav if (sbuf_new(&s, NULL, 4096, 0) == NULL) 962285648f9SCameron Grant return ENXIO; 963987e5972SCameron Grant bufptr = 0; 964285648f9SCameron Grant err = (status_init(&s) > 0)? 0 : ENOMEM; 965285648f9SCameron Grant if (!err) 966285648f9SCameron Grant status_open = 1; 967285648f9SCameron Grant return err; 968285648f9SCameron Grant 969285648f9SCameron Grant case 1: /* close */ 970285648f9SCameron Grant if (!status_open) 971285648f9SCameron Grant return EBADF; 972285648f9SCameron Grant sbuf_delete(&s); 973285648f9SCameron Grant status_open = 0; 974285648f9SCameron Grant return 0; 975285648f9SCameron Grant 976285648f9SCameron Grant case 2: 977285648f9SCameron Grant if (!status_open) 978285648f9SCameron Grant return EBADF; 979285648f9SCameron Grant l = min(buf->uio_resid, sbuf_len(&s) - bufptr); 980285648f9SCameron Grant err = (l > 0)? uiomove(sbuf_data(&s) + bufptr, l, buf) : 0; 981285648f9SCameron Grant bufptr += l; 982285648f9SCameron Grant return err; 983285648f9SCameron Grant 984285648f9SCameron Grant case 3: 985285648f9SCameron Grant return status_open; 986987e5972SCameron Grant } 987987e5972SCameron Grant 988285648f9SCameron Grant return EBADF; 989987e5972SCameron Grant } 99033dbf14aSCameron Grant 99133dbf14aSCameron Grant static int 99233dbf14aSCameron Grant sndpcm_modevent(module_t mod, int type, void *data) 99333dbf14aSCameron Grant { 99433dbf14aSCameron Grant 99533dbf14aSCameron Grant switch (type) { 99633dbf14aSCameron Grant case MOD_LOAD: 99733dbf14aSCameron Grant break; 99833dbf14aSCameron Grant case MOD_UNLOAD: 999285648f9SCameron Grant if (do_status(3, NULL)) 1000c9b53085SCameron Grant return EBUSY; 100133dbf14aSCameron Grant if (status_dev) 100233dbf14aSCameron Grant destroy_dev(status_dev); 100333dbf14aSCameron Grant status_dev = 0; 100433dbf14aSCameron Grant break; 100533dbf14aSCameron Grant default: 100633dbf14aSCameron Grant break; 100733dbf14aSCameron Grant } 100833dbf14aSCameron Grant return 0; 100933dbf14aSCameron Grant } 101033dbf14aSCameron Grant 101133dbf14aSCameron Grant static moduledata_t sndpcm_mod = { 101233dbf14aSCameron Grant "snd_pcm", 101333dbf14aSCameron Grant sndpcm_modevent, 101433dbf14aSCameron Grant NULL 101533dbf14aSCameron Grant }; 101633dbf14aSCameron Grant DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 101733dbf14aSCameron Grant MODULE_VERSION(snd_pcm, PCM_MODVER); 1018