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> 317c438dbeSCameron Grant #include <sys/sysctl.h> 32987e5972SCameron Grant 3333dbf14aSCameron Grant static dev_t status_dev = 0; 34987e5972SCameron Grant static int status_isopen = 0; 35987e5972SCameron Grant static int status_init(char *buf, int size); 36987e5972SCameron Grant static int status_read(struct uio *buf); 37987e5972SCameron Grant 38987e5972SCameron Grant static d_open_t sndopen; 39987e5972SCameron Grant static d_close_t sndclose; 40987e5972SCameron Grant static d_ioctl_t sndioctl; 41987e5972SCameron Grant static d_read_t sndread; 42987e5972SCameron Grant static d_write_t sndwrite; 43987e5972SCameron Grant static d_mmap_t sndmmap; 44987e5972SCameron Grant static d_poll_t sndpoll; 45987e5972SCameron Grant 46987e5972SCameron Grant #define CDEV_MAJOR 30 47987e5972SCameron Grant static struct cdevsw snd_cdevsw = { 48987e5972SCameron Grant /* open */ sndopen, 49987e5972SCameron Grant /* close */ sndclose, 50987e5972SCameron Grant /* read */ sndread, 51987e5972SCameron Grant /* write */ sndwrite, 52987e5972SCameron Grant /* ioctl */ sndioctl, 53987e5972SCameron Grant /* poll */ sndpoll, 54987e5972SCameron Grant /* mmap */ sndmmap, 55987e5972SCameron Grant /* strategy */ nostrategy, 56987e5972SCameron Grant /* name */ "snd", 57987e5972SCameron Grant /* maj */ CDEV_MAJOR, 58987e5972SCameron Grant /* dump */ nodump, 59987e5972SCameron Grant /* psize */ nopsize, 60987e5972SCameron Grant /* flags */ 0, 61987e5972SCameron Grant /* bmaj */ -1 62987e5972SCameron Grant }; 63987e5972SCameron Grant 64dd186369SCameron Grant /* 65dd186369SCameron Grant PROPOSAL: 66987e5972SCameron Grant each unit needs: 67987e5972SCameron Grant status, mixer, dsp, dspW, audio, sequencer, midi-in, seq2, sndproc = 9 devices 68987e5972SCameron Grant dspW and audio are deprecated. 69987e5972SCameron Grant dsp needs min 64 channels, will give it 256 70987e5972SCameron Grant 71dd186369SCameron Grant minor = (unit << 20) + (dev << 16) + channel 72dd186369SCameron Grant currently minor = (channel << 16) + (unit << 4) + dev 73987e5972SCameron Grant 74987e5972SCameron Grant nomenclature: 75987e5972SCameron Grant /dev/pcmX/dsp.(0..255) 76987e5972SCameron Grant /dev/pcmX/dspW 77987e5972SCameron Grant /dev/pcmX/audio 78987e5972SCameron Grant /dev/pcmX/status 79987e5972SCameron Grant /dev/pcmX/mixer 80987e5972SCameron Grant [etc.] 81987e5972SCameron Grant */ 82987e5972SCameron Grant 83987e5972SCameron Grant #define PCMMINOR(x) (minor(x)) 84a618cffeSCameron Grant #define PCMCHAN(x) ((PCMMINOR(x) & 0x00ff0000) >> 16) 85987e5972SCameron Grant #define PCMUNIT(x) ((PCMMINOR(x) & 0x000000f0) >> 4) 86987e5972SCameron Grant #define PCMDEV(x) (PCMMINOR(x) & 0x0000000f) 87dd186369SCameron Grant #define PCMMKMINOR(u, d, c) ((((c) & 0xff) << 16) | (((u) & 0x0f) << 4) | ((d) & 0x0f)) 88987e5972SCameron Grant 89987e5972SCameron Grant static devclass_t pcm_devclass; 9082db23e2SCameron Grant 9182db23e2SCameron Grant #ifdef USING_DEVFS 927c438dbeSCameron Grant int snd_unit; 93fa465e99SJohn Baldwin TUNABLE_INT_DECL("hw.snd.unit", 0, snd_unit); 9482db23e2SCameron Grant #endif 95987e5972SCameron Grant 9682db23e2SCameron Grant SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver"); 9782db23e2SCameron Grant 9837209180SCameron Grant #ifndef USING_MUTEX 9937209180SCameron Grant #define SPLSTACK_MAX 32 10037209180SCameron Grant struct splstack { 10137209180SCameron Grant int depth; 10237209180SCameron Grant u_int32_t stack[SPLSTACK_MAX]; 10337209180SCameron Grant }; 10437209180SCameron Grant #endif 10537209180SCameron Grant 10637209180SCameron Grant void * 10737209180SCameron Grant snd_mtxcreate(const char *desc) 10837209180SCameron Grant { 10937209180SCameron Grant #ifdef USING_MUTEX 11037209180SCameron Grant struct mtx *m; 11137209180SCameron Grant 11237209180SCameron Grant m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); 11337209180SCameron Grant if (m == NULL) 11437209180SCameron Grant return NULL; 11537209180SCameron Grant mtx_init(m, desc, MTX_RECURSE); 11637209180SCameron Grant return m; 11737209180SCameron Grant #else 11837209180SCameron Grant struct splstack *s; 11937209180SCameron Grant 12037209180SCameron Grant s = malloc(sizeof(*s), M_DEVBUF, M_WAITOK | M_ZERO); 12137209180SCameron Grant s->depth = 0; 12237209180SCameron Grant return s; 12337209180SCameron Grant #endif 12437209180SCameron Grant } 12537209180SCameron Grant 12637209180SCameron Grant void 12737209180SCameron Grant snd_mtxfree(void *m) 12837209180SCameron Grant { 12937209180SCameron Grant #ifdef USING_MUTEX 13037209180SCameron Grant struct mtx *mtx = m; 13137209180SCameron Grant 13237209180SCameron Grant mtx_assert(mtx, MA_OWNED); 13337209180SCameron Grant mtx_destroy(mtx); 13437209180SCameron Grant free(mtx, M_DEVBUF); 13537209180SCameron Grant #else 13637209180SCameron Grant struct splstack *s = m; 13737209180SCameron Grant 13837209180SCameron Grant free(s, M_DEVBUF); 13937209180SCameron Grant #endif 14037209180SCameron Grant } 14137209180SCameron Grant 14237209180SCameron Grant void 14337209180SCameron Grant snd_mtxassert(void *m) 14437209180SCameron Grant { 14537209180SCameron Grant #ifdef USING_MUTEX 14637209180SCameron Grant struct mtx *mtx = m; 14737209180SCameron Grant 14837209180SCameron Grant mtx_assert(mtx, MA_OWNED); 14937209180SCameron Grant #else 15037209180SCameron Grant KASSERT(((struct splstack *)s)->depth > 0, ("splstack depth <= 0")); 15137209180SCameron Grant #endif 15237209180SCameron Grant } 15337209180SCameron Grant 15437209180SCameron Grant void 15537209180SCameron Grant snd_mtxlock(void *m) 15637209180SCameron Grant { 15737209180SCameron Grant #ifdef USING_MUTEX 15837209180SCameron Grant struct mtx *mtx = m; 15937209180SCameron Grant 16037209180SCameron Grant mtx_lock(mtx); 16137209180SCameron Grant #else 16237209180SCameron Grant struct splstack *s = m; 16337209180SCameron Grant 16437209180SCameron Grant KASSERT((s->depth >= 0) && (s->depth < SPLSTACK_MAX), ("bad depth %d", s->depth)); 16537209180SCameron Grant s->stack[s->depth++] = spltty(); 16637209180SCameron Grant #endif 16737209180SCameron Grant } 16837209180SCameron Grant 16937209180SCameron Grant void 17037209180SCameron Grant snd_mtxunlock(void *m) 17137209180SCameron Grant { 17237209180SCameron Grant #ifdef USING_MUTEX 17337209180SCameron Grant struct mtx *mtx = m; 17437209180SCameron Grant 17537209180SCameron Grant mtx_unlock(mtx); 17637209180SCameron Grant #else 17737209180SCameron Grant struct splstack *s = m; 17837209180SCameron Grant 17937209180SCameron Grant KASSERT((s->depth >= 1) && (s->depth < SPLSTACK_MAX), ("bad depth %d", s->depth)); 18037209180SCameron Grant splx(s->stack[s->depth--]); 18137209180SCameron Grant #endif 18237209180SCameron Grant } 18337209180SCameron Grant 18437209180SCameron Grant int 18537209180SCameron Grant snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) 18637209180SCameron Grant { 18737209180SCameron Grant #ifdef USING_MUTEX 18837209180SCameron Grant flags &= INTR_MPSAFE; 18937209180SCameron Grant flags |= INTR_TYPE_TTY; 19037209180SCameron Grant #else 19137209180SCameron Grant flags = INTR_TYPE_TTY; 19237209180SCameron Grant #endif 19337209180SCameron Grant return bus_setup_intr(dev, res, flags, hand, param, cookiep); 19437209180SCameron Grant } 19537209180SCameron Grant 19682db23e2SCameron Grant #ifdef USING_DEVFS 19733dbf14aSCameron Grant static void 19833dbf14aSCameron Grant pcm_makelinks(void *dummy) 199987e5972SCameron Grant { 20033dbf14aSCameron Grant int unit; 20133dbf14aSCameron Grant dev_t pdev; 20233dbf14aSCameron Grant static dev_t dsp = 0, dspW = 0, audio = 0, mixer = 0; 20333dbf14aSCameron Grant 204f776b5abSCameron Grant if (pcm_devclass == NULL || devfs_present == 0) 20533dbf14aSCameron Grant return; 20633dbf14aSCameron Grant if (dsp) { 20733dbf14aSCameron Grant destroy_dev(dsp); 20833dbf14aSCameron Grant dsp = 0; 209987e5972SCameron Grant } 21033dbf14aSCameron Grant if (dspW) { 21133dbf14aSCameron Grant destroy_dev(dspW); 21233dbf14aSCameron Grant dspW = 0; 21333dbf14aSCameron Grant } 21433dbf14aSCameron Grant if (audio) { 21533dbf14aSCameron Grant destroy_dev(audio); 21633dbf14aSCameron Grant audio = 0; 21733dbf14aSCameron Grant } 21833dbf14aSCameron Grant if (mixer) { 21933dbf14aSCameron Grant destroy_dev(mixer); 22033dbf14aSCameron Grant mixer = 0; 22133dbf14aSCameron Grant } 22233dbf14aSCameron Grant 22333dbf14aSCameron Grant unit = snd_unit; 22433dbf14aSCameron Grant if (unit < 0 || unit > devclass_get_maxunit(pcm_devclass)) 22533dbf14aSCameron Grant return; 22633dbf14aSCameron Grant if (devclass_get_softc(pcm_devclass, unit) == NULL) 22733dbf14aSCameron Grant return; 22833dbf14aSCameron Grant 22933dbf14aSCameron Grant pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, 0)); 23033dbf14aSCameron Grant dsp = make_dev_alias(pdev, "dsp"); 23133dbf14aSCameron Grant pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, 0)); 23233dbf14aSCameron Grant dspW = make_dev_alias(pdev, "dspW"); 23333dbf14aSCameron Grant pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, 0)); 23433dbf14aSCameron Grant audio = make_dev_alias(pdev, "audio"); 23533dbf14aSCameron Grant pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_CTL, 0)); 23633dbf14aSCameron Grant mixer = make_dev_alias(pdev, "mixer"); 23733dbf14aSCameron Grant } 23833dbf14aSCameron Grant 23933dbf14aSCameron Grant static int 24033dbf14aSCameron Grant sysctl_hw_sndunit(SYSCTL_HANDLER_ARGS) 24133dbf14aSCameron Grant { 24233dbf14aSCameron Grant int error, unit; 24333dbf14aSCameron Grant 24433dbf14aSCameron Grant unit = snd_unit; 24533dbf14aSCameron Grant error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); 24633dbf14aSCameron Grant if (error == 0 && req->newptr != NULL) { 24733dbf14aSCameron Grant snd_unit = unit; 24833dbf14aSCameron Grant pcm_makelinks(NULL); 24933dbf14aSCameron Grant } 25033dbf14aSCameron Grant return (error); 25133dbf14aSCameron Grant } 252b3b7ccfeSJohn Baldwin SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW, 25333dbf14aSCameron Grant 0, sizeof(int), sysctl_hw_sndunit, "I", ""); 25482db23e2SCameron Grant #endif 255987e5972SCameron Grant 256987e5972SCameron Grant int 2570f55ac6cSCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 258987e5972SCameron Grant { 25933dbf14aSCameron Grant int unit = device_get_unit(dev), idx; 260987e5972SCameron Grant snddev_info *d = device_get_softc(dev); 26133dbf14aSCameron Grant pcm_channel *chns, *ch; 26233dbf14aSCameron Grant char *dirs; 2630f55ac6cSCameron Grant int err; 264987e5972SCameron Grant 26533dbf14aSCameron Grant dirs = ((dir == PCMDIR_PLAY)? "play" : "record"); 26633dbf14aSCameron Grant chns = ((dir == PCMDIR_PLAY)? d->play : d->rec); 26733dbf14aSCameron Grant idx = ((dir == PCMDIR_PLAY)? d->playcount++ : d->reccount++); 26833dbf14aSCameron Grant 26933dbf14aSCameron Grant if (chns == NULL) { 27033dbf14aSCameron Grant device_printf(dev, "bad channel add (%s:%d)\n", dirs, idx); 2719c326820SCameron Grant return 1; 2729c326820SCameron Grant } 27333dbf14aSCameron Grant ch = &chns[idx]; 2740f55ac6cSCameron Grant ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK); 275e4d5b250SCameron Grant ch->parent = d; 2760f55ac6cSCameron Grant err = chn_init(ch, devinfo, dir); 2770f55ac6cSCameron Grant if (err) { 2780f55ac6cSCameron Grant device_printf(dev, "chn_init() for (%s:%d) failed: err = %d\n", dirs, idx, err); 279bbb5bf3dSCameron Grant return 1; 280bbb5bf3dSCameron Grant } 2817207eca6SCameron Grant make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount), 2827207eca6SCameron Grant UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", unit, d->chancount); 2837207eca6SCameron Grant make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount), 2847207eca6SCameron Grant UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", unit, d->chancount); 28533dbf14aSCameron Grant make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount), 28633dbf14aSCameron Grant UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", unit, d->chancount); 2877207eca6SCameron Grant /* XXX SND_DEV_NORESET? */ 288987e5972SCameron Grant d->chancount++; 28982db23e2SCameron Grant #ifdef USING_DEVFS 2906d97297aSJohn Baldwin if (d->chancount == 1) 29133dbf14aSCameron Grant pcm_makelinks(NULL); 29282db23e2SCameron Grant #endif 29333dbf14aSCameron Grant return 0; 29433dbf14aSCameron Grant } 29533dbf14aSCameron Grant 29633dbf14aSCameron Grant static int 29733dbf14aSCameron Grant pcm_killchan(device_t dev, int dir) 29833dbf14aSCameron Grant { 29933dbf14aSCameron Grant int unit = device_get_unit(dev), idx; 30033dbf14aSCameron Grant snddev_info *d = device_get_softc(dev); 30133dbf14aSCameron Grant pcm_channel *chns, *ch; 30233dbf14aSCameron Grant char *dirs; 30333dbf14aSCameron Grant dev_t pdev; 30433dbf14aSCameron Grant 30533dbf14aSCameron Grant dirs = ((dir == PCMDIR_PLAY)? "play" : "record"); 30633dbf14aSCameron Grant chns = ((dir == PCMDIR_PLAY)? d->play : d->rec); 30733dbf14aSCameron Grant idx = ((dir == PCMDIR_PLAY)? --d->playcount : --d->reccount); 30833dbf14aSCameron Grant 30933dbf14aSCameron Grant if (chns == NULL || idx < 0) { 31033dbf14aSCameron Grant device_printf(dev, "bad channel kill (%s:%d)\n", dirs, idx); 31133dbf14aSCameron Grant return 1; 31233dbf14aSCameron Grant } 31333dbf14aSCameron Grant ch = &chns[idx]; 31433dbf14aSCameron Grant if (chn_kill(ch)) { 31533dbf14aSCameron Grant device_printf(dev, "chn_kill() for (%s:%d) failed\n", dirs, idx); 31633dbf14aSCameron Grant return 1; 31733dbf14aSCameron Grant } 3180f55ac6cSCameron Grant kobj_delete(ch->methods, M_DEVBUF); 3190f55ac6cSCameron Grant ch->methods = NULL; 32033dbf14aSCameron Grant d->chancount--; 32133dbf14aSCameron Grant pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount)); 32233dbf14aSCameron Grant destroy_dev(pdev); 32333dbf14aSCameron Grant pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount)); 32433dbf14aSCameron Grant destroy_dev(pdev); 32533dbf14aSCameron Grant pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount)); 32633dbf14aSCameron Grant destroy_dev(pdev); 327987e5972SCameron Grant return 0; 328987e5972SCameron Grant } 329987e5972SCameron Grant 330987e5972SCameron Grant int 331987e5972SCameron Grant pcm_setstatus(device_t dev, char *str) 332987e5972SCameron Grant { 333987e5972SCameron Grant snddev_info *d = device_get_softc(dev); 334987e5972SCameron Grant strncpy(d->status, str, SND_STATUSLEN); 335987e5972SCameron Grant return 0; 336987e5972SCameron Grant } 337987e5972SCameron Grant 338987e5972SCameron Grant u_int32_t 339987e5972SCameron Grant pcm_getflags(device_t dev) 340987e5972SCameron Grant { 341987e5972SCameron Grant snddev_info *d = device_get_softc(dev); 342987e5972SCameron Grant return d->flags; 343987e5972SCameron Grant } 344987e5972SCameron Grant 345987e5972SCameron Grant void 346987e5972SCameron Grant pcm_setflags(device_t dev, u_int32_t val) 347987e5972SCameron Grant { 348987e5972SCameron Grant snddev_info *d = device_get_softc(dev); 349987e5972SCameron Grant d->flags = val; 350987e5972SCameron Grant } 351987e5972SCameron Grant 35239004e69SCameron Grant void * 35339004e69SCameron Grant pcm_getdevinfo(device_t dev) 35439004e69SCameron Grant { 35539004e69SCameron Grant snddev_info *d = device_get_softc(dev); 35639004e69SCameron Grant return d->devinfo; 35739004e69SCameron Grant } 35839004e69SCameron Grant 359987e5972SCameron Grant /* This is the generic init routine */ 360987e5972SCameron Grant int 361987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 362987e5972SCameron Grant { 363987e5972SCameron Grant int sz, unit = device_get_unit(dev); 364987e5972SCameron Grant snddev_info *d = device_get_softc(dev); 365987e5972SCameron Grant 366987e5972SCameron Grant if (!pcm_devclass) { 367987e5972SCameron Grant pcm_devclass = device_get_devclass(dev); 36833dbf14aSCameron Grant status_dev = make_dev(&snd_cdevsw, PCMMKMINOR(0, SND_DEV_STATUS, 0), 369083279e4SSeigo Tanimura UID_ROOT, GID_WHEEL, 0444, "sndstat"); 370083279e4SSeigo Tanimura } 3717207eca6SCameron Grant make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0), 37211346231SSeigo Tanimura UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit); 373e4d5b250SCameron Grant d->dev = dev; 374987e5972SCameron Grant d->devinfo = devinfo; 375987e5972SCameron Grant d->chancount = d->playcount = d->reccount = 0; 37633dbf14aSCameron Grant d->maxchans = numplay + numrec; 377987e5972SCameron Grant sz = (numplay + numrec) * sizeof(pcm_channel *); 378833f7023SCameron Grant 379833f7023SCameron Grant if (sz > 0) { 380987e5972SCameron Grant d->aplay = (pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT); 381987e5972SCameron Grant if (!d->aplay) goto no; 382833f7023SCameron Grant bzero(d->aplay, sz); 383833f7023SCameron Grant 384987e5972SCameron Grant d->arec = (pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT); 385987e5972SCameron Grant if (!d->arec) goto no; 386987e5972SCameron Grant bzero(d->arec, sz); 387bd18f334SCameron Grant 388bd18f334SCameron Grant sz = (numplay + numrec) * sizeof(int); 389bd18f334SCameron Grant d->ref = (int *)malloc(sz, M_DEVBUF, M_NOWAIT); 390bd18f334SCameron Grant if (!d->ref) goto no; 391bd18f334SCameron Grant bzero(d->ref, sz); 392833f7023SCameron Grant } 393987e5972SCameron Grant 394833f7023SCameron Grant if (numplay > 0) { 395987e5972SCameron Grant d->play = (pcm_channel *)malloc(numplay * sizeof(pcm_channel), 396987e5972SCameron Grant M_DEVBUF, M_NOWAIT); 397987e5972SCameron Grant if (!d->play) goto no; 398833f7023SCameron Grant bzero(d->play, numplay * sizeof(pcm_channel)); 3999c326820SCameron Grant } else 4009c326820SCameron Grant d->play = NULL; 401833f7023SCameron Grant 402833f7023SCameron Grant if (numrec > 0) { 403987e5972SCameron Grant d->rec = (pcm_channel *)malloc(numrec * sizeof(pcm_channel), 404987e5972SCameron Grant M_DEVBUF, M_NOWAIT); 405987e5972SCameron Grant if (!d->rec) goto no; 406987e5972SCameron Grant bzero(d->rec, numrec * sizeof(pcm_channel)); 4079c326820SCameron Grant } else 4089c326820SCameron Grant d->rec = NULL; 409987e5972SCameron Grant 41082db23e2SCameron Grant #ifdef SND_DYNSYSCTL 411cc486d80SJohn Baldwin sysctl_ctx_init(&d->sysctl_tree); 412cc486d80SJohn Baldwin d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree, 413a3e893e0SJohn Baldwin SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO, 414cc486d80SJohn Baldwin device_get_nameunit(dev), CTLFLAG_RD, 0, ""); 415a3e893e0SJohn Baldwin if (d->sysctl_tree_top == NULL) { 416cc486d80SJohn Baldwin sysctl_ctx_free(&d->sysctl_tree); 417cc486d80SJohn Baldwin goto no; 418cc486d80SJohn Baldwin } 41982db23e2SCameron Grant #endif 420cc486d80SJohn Baldwin 42117dbf677SCameron Grant if (numplay == 0 || numrec == 0) 42217dbf677SCameron Grant d->flags |= SD_F_SIMPLEX; 42317dbf677SCameron Grant 424987e5972SCameron Grant fkchan_setup(&d->fakechan); 425987e5972SCameron Grant chn_init(&d->fakechan, NULL, 0); 426987e5972SCameron Grant d->magic = MAGIC(unit); /* debugging... */ 427987e5972SCameron Grant 428987e5972SCameron Grant return 0; 429987e5972SCameron Grant no: 430987e5972SCameron Grant if (d->aplay) free(d->aplay, M_DEVBUF); 431987e5972SCameron Grant if (d->play) free(d->play, M_DEVBUF); 432987e5972SCameron Grant if (d->arec) free(d->arec, M_DEVBUF); 433987e5972SCameron Grant if (d->rec) free(d->rec, M_DEVBUF); 43433dbf14aSCameron Grant if (d->ref) free(d->ref, M_DEVBUF); 435987e5972SCameron Grant return ENXIO; 436987e5972SCameron Grant } 437987e5972SCameron Grant 43833dbf14aSCameron Grant int 43933dbf14aSCameron Grant pcm_unregister(device_t dev) 4407c438dbeSCameron Grant { 44133dbf14aSCameron Grant int r, i, unit = device_get_unit(dev); 44233dbf14aSCameron Grant snddev_info *d = device_get_softc(dev); 4437c438dbeSCameron Grant dev_t pdev; 4447c438dbeSCameron Grant 44582db23e2SCameron Grant #ifdef SND_DYNSYSCTL 446cc486d80SJohn Baldwin sysctl_remove_oid(d->sysctl_tree_top, 1, 1); 447cc486d80SJohn Baldwin d->sysctl_tree_top = NULL; 448cc486d80SJohn Baldwin sysctl_ctx_free(&d->sysctl_tree); 44982db23e2SCameron Grant #endif 450cc486d80SJohn Baldwin 45133dbf14aSCameron Grant r = 0; 45233dbf14aSCameron Grant for (i = 0; i < d->chancount; i++) 45333dbf14aSCameron Grant if (d->ref[i]) r = EBUSY; 454c9b53085SCameron Grant if (r) { 455c9b53085SCameron Grant device_printf(dev, "unregister: channel busy"); 456c9b53085SCameron Grant return r; 457c9b53085SCameron Grant } 4580f55ac6cSCameron Grant if (mixer_isbusy(d->mixer)) { 459c9b53085SCameron Grant device_printf(dev, "unregister: mixer busy"); 460c9b53085SCameron Grant return EBUSY; 461c9b53085SCameron Grant } 4627c438dbeSCameron Grant 4637c438dbeSCameron Grant pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_CTL, 0)); 46433dbf14aSCameron Grant destroy_dev(pdev); 46533dbf14aSCameron Grant mixer_uninit(dev); 4667c438dbeSCameron Grant 46733dbf14aSCameron Grant while (d->playcount > 0) 46833dbf14aSCameron Grant pcm_killchan(dev, PCMDIR_PLAY); 46933dbf14aSCameron Grant while (d->reccount > 0) 47033dbf14aSCameron Grant pcm_killchan(dev, PCMDIR_REC); 47133dbf14aSCameron Grant d->magic = 0; 4727c438dbeSCameron Grant 47333dbf14aSCameron Grant if (d->aplay) free(d->aplay, M_DEVBUF); 47433dbf14aSCameron Grant if (d->play) free(d->play, M_DEVBUF); 47533dbf14aSCameron Grant if (d->arec) free(d->arec, M_DEVBUF); 47633dbf14aSCameron Grant if (d->rec) free(d->rec, M_DEVBUF); 47733dbf14aSCameron Grant if (d->ref) free(d->ref, M_DEVBUF); 47833dbf14aSCameron Grant 4790f55ac6cSCameron Grant fkchan_kill(&d->fakechan); 48082db23e2SCameron Grant 48182db23e2SCameron Grant #ifdef USING_DEVFS 4827c438dbeSCameron Grant pcm_makelinks(NULL); 48382db23e2SCameron Grant #endif 48433dbf14aSCameron Grant return 0; 48533dbf14aSCameron Grant } 4867c438dbeSCameron Grant 487987e5972SCameron Grant /* 488987e5972SCameron Grant * a small utility function which, given a device number, returns 489987e5972SCameron Grant * a pointer to the associated snddev_info struct, and sets the unit 490987e5972SCameron Grant * number. 491987e5972SCameron Grant */ 492987e5972SCameron Grant static snddev_info * 493987e5972SCameron Grant get_snddev_info(dev_t i_dev, int *unit, int *dev, int *chan) 494987e5972SCameron Grant { 49533dbf14aSCameron Grant snddev_info *sc; 496987e5972SCameron Grant int u, d, c; 497987e5972SCameron Grant 498987e5972SCameron Grant u = PCMUNIT(i_dev); 499987e5972SCameron Grant d = PCMDEV(i_dev); 500987e5972SCameron Grant c = PCMCHAN(i_dev); 501987e5972SCameron Grant if (u > devclass_get_maxunit(pcm_devclass)) u = -1; 502987e5972SCameron Grant if (unit) *unit = u; 503987e5972SCameron Grant if (dev) *dev = d; 504987e5972SCameron Grant if (chan) *chan = c; 505987e5972SCameron Grant if (u < 0) return NULL; 506987e5972SCameron Grant 50733dbf14aSCameron Grant sc = devclass_get_softc(pcm_devclass, u); 50833dbf14aSCameron Grant if (sc == NULL || sc->magic == 0) return NULL; 50933dbf14aSCameron Grant 510987e5972SCameron Grant switch(d) { 511987e5972SCameron Grant case SND_DEV_CTL: /* /dev/mixer handled by pcm */ 512987e5972SCameron Grant case SND_DEV_STATUS: /* /dev/sndstat handled by pcm */ 513987e5972SCameron Grant case SND_DEV_DSP: 514987e5972SCameron Grant case SND_DEV_DSP16: 515987e5972SCameron Grant case SND_DEV_AUDIO: 51633dbf14aSCameron Grant return sc; 517987e5972SCameron Grant 518987e5972SCameron Grant case SND_DEV_SEQ: /* XXX when enabled... */ 519987e5972SCameron Grant case SND_DEV_SEQ2: 520987e5972SCameron Grant case SND_DEV_MIDIN: 521987e5972SCameron Grant case SND_DEV_SNDPROC: /* /dev/sndproc handled by pcm */ 522987e5972SCameron Grant default: 523987e5972SCameron Grant printf("unsupported subdevice %d\n", d); 524987e5972SCameron Grant return NULL; 525987e5972SCameron Grant } 526987e5972SCameron Grant } 527987e5972SCameron Grant 528987e5972SCameron Grant static int 529987e5972SCameron Grant sndopen(dev_t i_dev, int flags, int mode, struct proc *p) 530987e5972SCameron Grant { 531987e5972SCameron Grant int dev, unit, chan; 532987e5972SCameron Grant snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 533987e5972SCameron Grant 534987e5972SCameron Grant DEB(printf("open snd%d subdev %d flags 0x%08x mode 0x%08x\n", 535987e5972SCameron Grant unit, dev, flags, mode)); 536987e5972SCameron Grant 537987e5972SCameron Grant switch(dev) { 538987e5972SCameron Grant case SND_DEV_STATUS: 539987e5972SCameron Grant if (status_isopen) return EBUSY; 540987e5972SCameron Grant status_isopen = 1; 541987e5972SCameron Grant return 0; 542987e5972SCameron Grant 543987e5972SCameron Grant case SND_DEV_CTL: 5440f55ac6cSCameron Grant return d? mixer_busy(d->mixer, 1) : ENXIO; 545987e5972SCameron Grant 546987e5972SCameron Grant case SND_DEV_AUDIO: 547987e5972SCameron Grant case SND_DEV_DSP: 548987e5972SCameron Grant case SND_DEV_DSP16: 5495b78a734SCameron Grant case SND_DEV_NORESET: 550987e5972SCameron Grant return d? dsp_open(d, chan, flags, dev) : ENXIO; 551987e5972SCameron Grant 552987e5972SCameron Grant default: 553987e5972SCameron Grant return ENXIO; 554987e5972SCameron Grant } 555987e5972SCameron Grant } 556987e5972SCameron Grant 557987e5972SCameron Grant static int 558987e5972SCameron Grant sndclose(dev_t i_dev, int flags, int mode, struct proc *p) 559987e5972SCameron Grant { 560987e5972SCameron Grant int dev, unit, chan; 561987e5972SCameron Grant snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 562987e5972SCameron Grant 563987e5972SCameron Grant DEB(printf("close snd%d subdev %d\n", unit, dev)); 564987e5972SCameron Grant 565987e5972SCameron Grant switch(dev) { /* only those for which close makes sense */ 566987e5972SCameron Grant case SND_DEV_STATUS: 567987e5972SCameron Grant if (!status_isopen) return EBADF; 568987e5972SCameron Grant status_isopen = 0; 569987e5972SCameron Grant return 0; 570987e5972SCameron Grant 571987e5972SCameron Grant case SND_DEV_CTL: 5720f55ac6cSCameron Grant return d? mixer_busy(d->mixer, 0) : ENXIO; 573987e5972SCameron Grant 574987e5972SCameron Grant case SND_DEV_AUDIO: 575987e5972SCameron Grant case SND_DEV_DSP: 576987e5972SCameron Grant case SND_DEV_DSP16: 577987e5972SCameron Grant return d? dsp_close(d, chan, dev) : ENXIO; 578987e5972SCameron Grant 579987e5972SCameron Grant default: 580987e5972SCameron Grant return ENXIO; 581987e5972SCameron Grant } 582987e5972SCameron Grant } 583987e5972SCameron Grant 584987e5972SCameron Grant static int 585987e5972SCameron Grant sndread(dev_t i_dev, struct uio *buf, int flag) 586987e5972SCameron Grant { 587987e5972SCameron Grant int dev, unit, chan; 588987e5972SCameron Grant snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 589987e5972SCameron Grant DEB(printf("read snd%d subdev %d flag 0x%08x\n", unit, dev, flag)); 590987e5972SCameron Grant 591987e5972SCameron Grant switch(dev) { 592987e5972SCameron Grant case SND_DEV_STATUS: 593987e5972SCameron Grant return status_isopen? status_read(buf) : EBADF; 594987e5972SCameron Grant 595987e5972SCameron Grant case SND_DEV_AUDIO: 596987e5972SCameron Grant case SND_DEV_DSP: 597987e5972SCameron Grant case SND_DEV_DSP16: 598987e5972SCameron Grant return d? dsp_read(d, chan, buf, flag) : EBADF; 599987e5972SCameron Grant 600987e5972SCameron Grant default: 601987e5972SCameron Grant return ENXIO; 602987e5972SCameron Grant } 603987e5972SCameron Grant } 604987e5972SCameron Grant 605987e5972SCameron Grant static int 606987e5972SCameron Grant sndwrite(dev_t i_dev, struct uio *buf, int flag) 607987e5972SCameron Grant { 608987e5972SCameron Grant int dev, unit, chan; 609987e5972SCameron Grant snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 610987e5972SCameron Grant 611987e5972SCameron Grant DEB(printf("write snd%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag)); 612987e5972SCameron Grant 613987e5972SCameron Grant switch(dev) { /* only writeable devices */ 614987e5972SCameron Grant case SND_DEV_DSP: 615987e5972SCameron Grant case SND_DEV_DSP16: 616987e5972SCameron Grant case SND_DEV_AUDIO: 617987e5972SCameron Grant return d? dsp_write(d, chan, buf, flag) : EBADF; 618987e5972SCameron Grant 619987e5972SCameron Grant default: 620987e5972SCameron Grant return EPERM; /* for non-writeable devices ; */ 621987e5972SCameron Grant } 622987e5972SCameron Grant } 623987e5972SCameron Grant 624987e5972SCameron Grant static int 625987e5972SCameron Grant sndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p) 626987e5972SCameron Grant { 627987e5972SCameron Grant int dev, chan; 628987e5972SCameron Grant snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan); 629987e5972SCameron Grant 630987e5972SCameron Grant if (d == NULL) return ENXIO; 631987e5972SCameron Grant 632987e5972SCameron Grant switch(dev) { 633987e5972SCameron Grant case SND_DEV_CTL: 634987e5972SCameron Grant return mixer_ioctl(d, cmd, arg); 635987e5972SCameron Grant 636987e5972SCameron Grant case SND_DEV_AUDIO: 637987e5972SCameron Grant case SND_DEV_DSP: 638987e5972SCameron Grant case SND_DEV_DSP16: 6391ad869dbSCameron Grant if (IOCGROUP(cmd) == 'M') 6401ad869dbSCameron Grant return mixer_ioctl(d, cmd, arg); 6411ad869dbSCameron Grant else 642987e5972SCameron Grant return dsp_ioctl(d, chan, cmd, arg); 643987e5972SCameron Grant 644987e5972SCameron Grant default: 645987e5972SCameron Grant return ENXIO; 646987e5972SCameron Grant } 647987e5972SCameron Grant } 648987e5972SCameron Grant 649987e5972SCameron Grant static int 650987e5972SCameron Grant sndpoll(dev_t i_dev, int events, struct proc *p) 651987e5972SCameron Grant { 652987e5972SCameron Grant int dev, chan; 653987e5972SCameron Grant snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan); 654987e5972SCameron Grant 6550e25481fSCameron Grant DEB(printf("sndpoll d 0x%p dev 0x%04x events 0x%08x\n", d, dev, events)); 656987e5972SCameron Grant 657987e5972SCameron Grant if (d == NULL) return ENXIO; 658987e5972SCameron Grant 659987e5972SCameron Grant switch(dev) { 660987e5972SCameron Grant case SND_DEV_AUDIO: 661987e5972SCameron Grant case SND_DEV_DSP: 662987e5972SCameron Grant case SND_DEV_DSP16: 663987e5972SCameron Grant return dsp_poll(d, chan, events, p); 664987e5972SCameron Grant 665987e5972SCameron Grant default: 666987e5972SCameron Grant return (events & 667987e5972SCameron Grant (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)) | POLLHUP; 668987e5972SCameron Grant } 669987e5972SCameron Grant } 670987e5972SCameron Grant 671987e5972SCameron Grant /* 672987e5972SCameron Grant * The mmap interface allows access to the play and read buffer, 673987e5972SCameron Grant * plus the device descriptor. 674987e5972SCameron Grant * The various blocks are accessible at the following offsets: 675987e5972SCameron Grant * 676987e5972SCameron Grant * 0x00000000 ( 0 ) : write buffer ; 677987e5972SCameron Grant * 0x01000000 (16 MB) : read buffer ; 678987e5972SCameron Grant * 0x02000000 (32 MB) : device descriptor (dangerous!) 679987e5972SCameron Grant * 680987e5972SCameron Grant * WARNING: the mmap routines assume memory areas are aligned. This 681987e5972SCameron Grant * is true (probably) for the dma buffers, but likely false for the 682987e5972SCameron Grant * device descriptor. As a consequence, we do not know where it is 683987e5972SCameron Grant * located in the requested area. 684987e5972SCameron Grant */ 685987e5972SCameron Grant static int 686987e5972SCameron Grant sndmmap(dev_t i_dev, vm_offset_t offset, int nprot) 687987e5972SCameron Grant { 688987e5972SCameron Grant int unit, dev, chan; 689987e5972SCameron Grant snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 690987e5972SCameron Grant 691987e5972SCameron Grant DEB(printf("sndmmap d 0x%p dev 0x%04x ofs 0x%08x nprot 0x%08x\n", 692987e5972SCameron Grant d, dev, offset, nprot)); 693987e5972SCameron Grant 694987e5972SCameron Grant if (d == NULL || nprot & PROT_EXEC) return -1; /* forbidden */ 695987e5972SCameron Grant 696987e5972SCameron Grant switch(dev) { 697987e5972SCameron Grant case SND_DEV_AUDIO: 698987e5972SCameron Grant case SND_DEV_DSP: 699987e5972SCameron Grant case SND_DEV_DSP16: 700987e5972SCameron Grant return dsp_mmap(d, chan, offset, nprot); 701987e5972SCameron Grant 702987e5972SCameron Grant default: 703987e5972SCameron Grant return -1; 704987e5972SCameron Grant } 705987e5972SCameron Grant } 706987e5972SCameron Grant 707987e5972SCameron Grant static int 708987e5972SCameron Grant status_init(char *buf, int size) 709987e5972SCameron Grant { 710987e5972SCameron Grant int i; 711987e5972SCameron Grant device_t dev; 712987e5972SCameron Grant snddev_info *d; 713987e5972SCameron Grant 714987e5972SCameron Grant snprintf(buf, size, "FreeBSD Audio Driver (newpcm) %s %s\n" 715987e5972SCameron Grant "Installed devices:\n", __DATE__, __TIME__); 716987e5972SCameron Grant 717987e5972SCameron Grant for (i = 0; i <= devclass_get_maxunit(pcm_devclass); i++) { 71833dbf14aSCameron Grant d = devclass_get_softc(pcm_devclass, i); 719987e5972SCameron Grant if (!d) continue; 720987e5972SCameron Grant dev = devclass_get_device(pcm_devclass, i); 721bf8ca271SCameron Grant if (1) { 722bf8ca271SCameron Grant snprintf(buf + strlen(buf), size - strlen(buf), 723bf8ca271SCameron Grant "pcm%d: <%s> %s", 724bf8ca271SCameron Grant i, device_get_desc(dev), d->status); 725bf8ca271SCameron Grant if (d->chancount > 0) 726bf8ca271SCameron Grant snprintf(buf + strlen(buf), size - strlen(buf), 727bf8ca271SCameron Grant " (%dp/%dr channels%s)\n", 728987e5972SCameron Grant d->playcount, d->reccount, 729987e5972SCameron Grant (!(d->flags & SD_F_SIMPLEX))? " duplex" : ""); 730bf8ca271SCameron Grant else 731bf8ca271SCameron Grant snprintf(buf + strlen(buf), size - strlen(buf), 7329bc50208SCameron Grant " (mixer only)\n"); 733bf8ca271SCameron Grant } 734987e5972SCameron Grant } 735987e5972SCameron Grant return strlen(buf); 736987e5972SCameron Grant } 737987e5972SCameron Grant 738987e5972SCameron Grant static int 739987e5972SCameron Grant status_read(struct uio *buf) 740987e5972SCameron Grant { 741987e5972SCameron Grant static char status_buf[4096]; 742987e5972SCameron Grant static int bufptr = 0, buflen = 0; 743987e5972SCameron Grant int l; 744987e5972SCameron Grant 745987e5972SCameron Grant if (status_isopen == 1) { 746987e5972SCameron Grant status_isopen++; 747987e5972SCameron Grant bufptr = 0; 748987e5972SCameron Grant buflen = status_init(status_buf, sizeof status_buf); 749987e5972SCameron Grant } 750987e5972SCameron Grant 751987e5972SCameron Grant l = min(buf->uio_resid, buflen - bufptr); 752987e5972SCameron Grant bufptr += l; 753987e5972SCameron Grant return (l > 0)? uiomove(status_buf + bufptr - l, l, buf) : 0; 754987e5972SCameron Grant } 75533dbf14aSCameron Grant 75633dbf14aSCameron Grant static int 75733dbf14aSCameron Grant sndpcm_modevent(module_t mod, int type, void *data) 75833dbf14aSCameron Grant { 75933dbf14aSCameron Grant 76033dbf14aSCameron Grant switch (type) { 76133dbf14aSCameron Grant case MOD_LOAD: 76233dbf14aSCameron Grant break; 76333dbf14aSCameron Grant case MOD_UNLOAD: 764c9b53085SCameron Grant if (status_isopen) 765c9b53085SCameron Grant return EBUSY; 76633dbf14aSCameron Grant if (status_dev) 76733dbf14aSCameron Grant destroy_dev(status_dev); 76833dbf14aSCameron Grant status_dev = 0; 76933dbf14aSCameron Grant break; 77033dbf14aSCameron Grant default: 77133dbf14aSCameron Grant break; 77233dbf14aSCameron Grant } 77333dbf14aSCameron Grant return 0; 77433dbf14aSCameron Grant } 77533dbf14aSCameron Grant 77633dbf14aSCameron Grant static moduledata_t sndpcm_mod = { 77733dbf14aSCameron Grant "snd_pcm", 77833dbf14aSCameron Grant sndpcm_modevent, 77933dbf14aSCameron Grant NULL 78033dbf14aSCameron Grant }; 78133dbf14aSCameron Grant DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 78233dbf14aSCameron Grant MODULE_VERSION(snd_pcm, PCM_MODVER); 783