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; 907c438dbeSCameron Grant int snd_unit; 917c438dbeSCameron Grant TUNABLE_INT_DECL("hw.sndunit", 0, snd_unit); 92987e5972SCameron Grant 9333dbf14aSCameron Grant static void 9433dbf14aSCameron Grant pcm_makelinks(void *dummy) 95987e5972SCameron Grant { 9633dbf14aSCameron Grant int unit; 9733dbf14aSCameron Grant dev_t pdev; 9833dbf14aSCameron Grant static dev_t dsp = 0, dspW = 0, audio = 0, mixer = 0; 9933dbf14aSCameron Grant 100f776b5abSCameron Grant if (pcm_devclass == NULL || devfs_present == 0) 10133dbf14aSCameron Grant return; 10233dbf14aSCameron Grant if (dsp) { 10333dbf14aSCameron Grant destroy_dev(dsp); 10433dbf14aSCameron Grant dsp = 0; 105987e5972SCameron Grant } 10633dbf14aSCameron Grant if (dspW) { 10733dbf14aSCameron Grant destroy_dev(dspW); 10833dbf14aSCameron Grant dspW = 0; 10933dbf14aSCameron Grant } 11033dbf14aSCameron Grant if (audio) { 11133dbf14aSCameron Grant destroy_dev(audio); 11233dbf14aSCameron Grant audio = 0; 11333dbf14aSCameron Grant } 11433dbf14aSCameron Grant if (mixer) { 11533dbf14aSCameron Grant destroy_dev(mixer); 11633dbf14aSCameron Grant mixer = 0; 11733dbf14aSCameron Grant } 11833dbf14aSCameron Grant 11933dbf14aSCameron Grant unit = snd_unit; 12033dbf14aSCameron Grant if (unit < 0 || unit > devclass_get_maxunit(pcm_devclass)) 12133dbf14aSCameron Grant return; 12233dbf14aSCameron Grant if (devclass_get_softc(pcm_devclass, unit) == NULL) 12333dbf14aSCameron Grant return; 12433dbf14aSCameron Grant 12533dbf14aSCameron Grant pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, 0)); 12633dbf14aSCameron Grant dsp = make_dev_alias(pdev, "dsp"); 12733dbf14aSCameron Grant pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, 0)); 12833dbf14aSCameron Grant dspW = make_dev_alias(pdev, "dspW"); 12933dbf14aSCameron Grant pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, 0)); 13033dbf14aSCameron Grant audio = make_dev_alias(pdev, "audio"); 13133dbf14aSCameron Grant pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_CTL, 0)); 13233dbf14aSCameron Grant mixer = make_dev_alias(pdev, "mixer"); 13333dbf14aSCameron Grant } 13433dbf14aSCameron Grant 13533dbf14aSCameron Grant static int 13633dbf14aSCameron Grant sysctl_hw_sndunit(SYSCTL_HANDLER_ARGS) 13733dbf14aSCameron Grant { 13833dbf14aSCameron Grant int error, unit; 13933dbf14aSCameron Grant 14033dbf14aSCameron Grant unit = snd_unit; 14133dbf14aSCameron Grant error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); 14233dbf14aSCameron Grant if (error == 0 && req->newptr != NULL) { 14333dbf14aSCameron Grant snd_unit = unit; 14433dbf14aSCameron Grant pcm_makelinks(NULL); 14533dbf14aSCameron Grant } 14633dbf14aSCameron Grant return (error); 14733dbf14aSCameron Grant } 14833dbf14aSCameron Grant SYSCTL_PROC(_hw, OID_AUTO, sndunit, CTLTYPE_INT | CTLFLAG_RW, 14933dbf14aSCameron Grant 0, sizeof(int), sysctl_hw_sndunit, "I", ""); 150987e5972SCameron Grant 151987e5972SCameron Grant int 152987e5972SCameron Grant pcm_addchan(device_t dev, int dir, pcm_channel *templ, void *devinfo) 153987e5972SCameron Grant { 15433dbf14aSCameron Grant int unit = device_get_unit(dev), idx; 155987e5972SCameron Grant snddev_info *d = device_get_softc(dev); 15633dbf14aSCameron Grant pcm_channel *chns, *ch; 15733dbf14aSCameron Grant char *dirs; 158987e5972SCameron Grant 15933dbf14aSCameron Grant dirs = ((dir == PCMDIR_PLAY)? "play" : "record"); 16033dbf14aSCameron Grant chns = ((dir == PCMDIR_PLAY)? d->play : d->rec); 16133dbf14aSCameron Grant idx = ((dir == PCMDIR_PLAY)? d->playcount++ : d->reccount++); 16233dbf14aSCameron Grant 16333dbf14aSCameron Grant if (chns == NULL) { 16433dbf14aSCameron Grant device_printf(dev, "bad channel add (%s:%d)\n", dirs, idx); 1659c326820SCameron Grant return 1; 1669c326820SCameron Grant } 16733dbf14aSCameron Grant ch = &chns[idx]; 168987e5972SCameron Grant *ch = *templ; 169e4d5b250SCameron Grant ch->parent = d; 170bbb5bf3dSCameron Grant if (chn_init(ch, devinfo, dir)) { 17133dbf14aSCameron Grant device_printf(dev, "chn_init() for (%s:%d) failed\n", dirs, idx); 172bbb5bf3dSCameron Grant return 1; 173bbb5bf3dSCameron Grant } 1747207eca6SCameron Grant make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount), 1757207eca6SCameron Grant UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", unit, d->chancount); 1767207eca6SCameron Grant make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount), 1777207eca6SCameron Grant UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", unit, d->chancount); 17833dbf14aSCameron Grant make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount), 17933dbf14aSCameron Grant UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", unit, d->chancount); 1807207eca6SCameron Grant /* XXX SND_DEV_NORESET? */ 181987e5972SCameron Grant d->chancount++; 18233dbf14aSCameron Grant if (d->chancount == d->maxchans) 18333dbf14aSCameron Grant pcm_makelinks(NULL); 18433dbf14aSCameron Grant return 0; 18533dbf14aSCameron Grant } 18633dbf14aSCameron Grant 18733dbf14aSCameron Grant static int 18833dbf14aSCameron Grant pcm_killchan(device_t dev, int dir) 18933dbf14aSCameron Grant { 19033dbf14aSCameron Grant int unit = device_get_unit(dev), idx; 19133dbf14aSCameron Grant snddev_info *d = device_get_softc(dev); 19233dbf14aSCameron Grant pcm_channel *chns, *ch; 19333dbf14aSCameron Grant char *dirs; 19433dbf14aSCameron Grant dev_t pdev; 19533dbf14aSCameron Grant 19633dbf14aSCameron Grant dirs = ((dir == PCMDIR_PLAY)? "play" : "record"); 19733dbf14aSCameron Grant chns = ((dir == PCMDIR_PLAY)? d->play : d->rec); 19833dbf14aSCameron Grant idx = ((dir == PCMDIR_PLAY)? --d->playcount : --d->reccount); 19933dbf14aSCameron Grant 20033dbf14aSCameron Grant if (chns == NULL || idx < 0) { 20133dbf14aSCameron Grant device_printf(dev, "bad channel kill (%s:%d)\n", dirs, idx); 20233dbf14aSCameron Grant return 1; 20333dbf14aSCameron Grant } 20433dbf14aSCameron Grant ch = &chns[idx]; 20533dbf14aSCameron Grant if (chn_kill(ch)) { 20633dbf14aSCameron Grant device_printf(dev, "chn_kill() for (%s:%d) failed\n", dirs, idx); 20733dbf14aSCameron Grant return 1; 20833dbf14aSCameron Grant } 20933dbf14aSCameron Grant d->chancount--; 21033dbf14aSCameron Grant pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount)); 21133dbf14aSCameron Grant destroy_dev(pdev); 21233dbf14aSCameron Grant pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount)); 21333dbf14aSCameron Grant destroy_dev(pdev); 21433dbf14aSCameron Grant pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount)); 21533dbf14aSCameron Grant destroy_dev(pdev); 216987e5972SCameron Grant return 0; 217987e5972SCameron Grant } 218987e5972SCameron Grant 219987e5972SCameron Grant int 220987e5972SCameron Grant pcm_setstatus(device_t dev, char *str) 221987e5972SCameron Grant { 222987e5972SCameron Grant snddev_info *d = device_get_softc(dev); 223987e5972SCameron Grant strncpy(d->status, str, SND_STATUSLEN); 224987e5972SCameron Grant return 0; 225987e5972SCameron Grant } 226987e5972SCameron Grant 227987e5972SCameron Grant u_int32_t 228987e5972SCameron Grant pcm_getflags(device_t dev) 229987e5972SCameron Grant { 230987e5972SCameron Grant snddev_info *d = device_get_softc(dev); 231987e5972SCameron Grant return d->flags; 232987e5972SCameron Grant } 233987e5972SCameron Grant 234987e5972SCameron Grant void 235987e5972SCameron Grant pcm_setflags(device_t dev, u_int32_t val) 236987e5972SCameron Grant { 237987e5972SCameron Grant snddev_info *d = device_get_softc(dev); 238987e5972SCameron Grant d->flags = val; 239987e5972SCameron Grant } 240987e5972SCameron Grant 24139004e69SCameron Grant void * 24239004e69SCameron Grant pcm_getdevinfo(device_t dev) 24339004e69SCameron Grant { 24439004e69SCameron Grant snddev_info *d = device_get_softc(dev); 24539004e69SCameron Grant return d->devinfo; 24639004e69SCameron Grant } 24739004e69SCameron Grant 2480927bf43SCameron Grant void 2490927bf43SCameron Grant pcm_setswap(device_t dev, pcm_swap_t *swap) 2500927bf43SCameron Grant { 2510927bf43SCameron Grant snddev_info *d = device_get_softc(dev); 2520927bf43SCameron Grant d->swap = swap; 2530927bf43SCameron Grant } 25433dbf14aSCameron Grant 255987e5972SCameron Grant /* This is the generic init routine */ 256987e5972SCameron Grant int 257987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 258987e5972SCameron Grant { 259987e5972SCameron Grant int sz, unit = device_get_unit(dev); 260987e5972SCameron Grant snddev_info *d = device_get_softc(dev); 261987e5972SCameron Grant 262987e5972SCameron Grant if (!pcm_devclass) { 263987e5972SCameron Grant pcm_devclass = device_get_devclass(dev); 26433dbf14aSCameron Grant status_dev = make_dev(&snd_cdevsw, PCMMKMINOR(0, SND_DEV_STATUS, 0), 265083279e4SSeigo Tanimura UID_ROOT, GID_WHEEL, 0444, "sndstat"); 266083279e4SSeigo Tanimura } 2677207eca6SCameron Grant make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0), 26811346231SSeigo Tanimura UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit); 269e4d5b250SCameron Grant d->dev = dev; 270987e5972SCameron Grant d->devinfo = devinfo; 271987e5972SCameron Grant d->chancount = d->playcount = d->reccount = 0; 27233dbf14aSCameron Grant d->maxchans = numplay + numrec; 273987e5972SCameron Grant sz = (numplay + numrec) * sizeof(pcm_channel *); 274833f7023SCameron Grant 275833f7023SCameron Grant if (sz > 0) { 276987e5972SCameron Grant d->aplay = (pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT); 277987e5972SCameron Grant if (!d->aplay) goto no; 278833f7023SCameron Grant bzero(d->aplay, sz); 279833f7023SCameron Grant 280987e5972SCameron Grant d->arec = (pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT); 281987e5972SCameron Grant if (!d->arec) goto no; 282987e5972SCameron Grant bzero(d->arec, sz); 283bd18f334SCameron Grant 284bd18f334SCameron Grant sz = (numplay + numrec) * sizeof(int); 285bd18f334SCameron Grant d->ref = (int *)malloc(sz, M_DEVBUF, M_NOWAIT); 286bd18f334SCameron Grant if (!d->ref) goto no; 287bd18f334SCameron Grant bzero(d->ref, sz); 288833f7023SCameron Grant } 289987e5972SCameron Grant 290833f7023SCameron Grant if (numplay > 0) { 291987e5972SCameron Grant d->play = (pcm_channel *)malloc(numplay * sizeof(pcm_channel), 292987e5972SCameron Grant M_DEVBUF, M_NOWAIT); 293987e5972SCameron Grant if (!d->play) goto no; 294833f7023SCameron Grant bzero(d->play, numplay * sizeof(pcm_channel)); 2959c326820SCameron Grant } else 2969c326820SCameron Grant d->play = NULL; 297833f7023SCameron Grant 298833f7023SCameron Grant if (numrec > 0) { 299987e5972SCameron Grant d->rec = (pcm_channel *)malloc(numrec * sizeof(pcm_channel), 300987e5972SCameron Grant M_DEVBUF, M_NOWAIT); 301987e5972SCameron Grant if (!d->rec) goto no; 302987e5972SCameron Grant bzero(d->rec, numrec * sizeof(pcm_channel)); 3039c326820SCameron Grant } else 3049c326820SCameron Grant d->rec = NULL; 305987e5972SCameron Grant 30617dbf677SCameron Grant if (numplay == 0 || numrec == 0) 30717dbf677SCameron Grant d->flags |= SD_F_SIMPLEX; 30817dbf677SCameron Grant 309987e5972SCameron Grant fkchan_setup(&d->fakechan); 310987e5972SCameron Grant chn_init(&d->fakechan, NULL, 0); 311987e5972SCameron Grant d->magic = MAGIC(unit); /* debugging... */ 3120927bf43SCameron Grant d->swap = NULL; 313987e5972SCameron Grant 314987e5972SCameron Grant return 0; 315987e5972SCameron Grant no: 316987e5972SCameron Grant if (d->aplay) free(d->aplay, M_DEVBUF); 317987e5972SCameron Grant if (d->play) free(d->play, M_DEVBUF); 318987e5972SCameron Grant if (d->arec) free(d->arec, M_DEVBUF); 319987e5972SCameron Grant if (d->rec) free(d->rec, M_DEVBUF); 32033dbf14aSCameron Grant if (d->ref) free(d->ref, M_DEVBUF); 321987e5972SCameron Grant return ENXIO; 322987e5972SCameron Grant } 323987e5972SCameron Grant 32433dbf14aSCameron Grant int 32533dbf14aSCameron Grant pcm_unregister(device_t dev) 3267c438dbeSCameron Grant { 32733dbf14aSCameron Grant int r, i, unit = device_get_unit(dev); 32833dbf14aSCameron Grant snddev_info *d = device_get_softc(dev); 3297c438dbeSCameron Grant dev_t pdev; 3307c438dbeSCameron Grant 33133dbf14aSCameron Grant r = 0; 33233dbf14aSCameron Grant for (i = 0; i < d->chancount; i++) 33333dbf14aSCameron Grant if (d->ref[i]) r = EBUSY; 334c9b53085SCameron Grant if (r) { 335c9b53085SCameron Grant device_printf(dev, "unregister: channel busy"); 336c9b53085SCameron Grant return r; 337c9b53085SCameron Grant } 338c9b53085SCameron Grant if (mixer_isbusy(d)) { 339c9b53085SCameron Grant device_printf(dev, "unregister: mixer busy"); 340c9b53085SCameron Grant return EBUSY; 341c9b53085SCameron Grant } 3427c438dbeSCameron Grant 3437c438dbeSCameron Grant pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_CTL, 0)); 34433dbf14aSCameron Grant destroy_dev(pdev); 34533dbf14aSCameron Grant mixer_uninit(dev); 3467c438dbeSCameron Grant 34733dbf14aSCameron Grant while (d->playcount > 0) 34833dbf14aSCameron Grant pcm_killchan(dev, PCMDIR_PLAY); 34933dbf14aSCameron Grant while (d->reccount > 0) 35033dbf14aSCameron Grant pcm_killchan(dev, PCMDIR_REC); 35133dbf14aSCameron Grant d->magic = 0; 3527c438dbeSCameron Grant 35333dbf14aSCameron Grant if (d->aplay) free(d->aplay, M_DEVBUF); 35433dbf14aSCameron Grant if (d->play) free(d->play, M_DEVBUF); 35533dbf14aSCameron Grant if (d->arec) free(d->arec, M_DEVBUF); 35633dbf14aSCameron Grant if (d->rec) free(d->rec, M_DEVBUF); 35733dbf14aSCameron Grant if (d->ref) free(d->ref, M_DEVBUF); 35833dbf14aSCameron Grant 3597c438dbeSCameron Grant pcm_makelinks(NULL); 36033dbf14aSCameron Grant return 0; 36133dbf14aSCameron Grant } 3627c438dbeSCameron Grant 363987e5972SCameron Grant /* 364987e5972SCameron Grant * a small utility function which, given a device number, returns 365987e5972SCameron Grant * a pointer to the associated snddev_info struct, and sets the unit 366987e5972SCameron Grant * number. 367987e5972SCameron Grant */ 368987e5972SCameron Grant static snddev_info * 369987e5972SCameron Grant get_snddev_info(dev_t i_dev, int *unit, int *dev, int *chan) 370987e5972SCameron Grant { 37133dbf14aSCameron Grant snddev_info *sc; 372987e5972SCameron Grant int u, d, c; 373987e5972SCameron Grant 374987e5972SCameron Grant u = PCMUNIT(i_dev); 375987e5972SCameron Grant d = PCMDEV(i_dev); 376987e5972SCameron Grant c = PCMCHAN(i_dev); 377987e5972SCameron Grant if (u > devclass_get_maxunit(pcm_devclass)) u = -1; 378987e5972SCameron Grant if (unit) *unit = u; 379987e5972SCameron Grant if (dev) *dev = d; 380987e5972SCameron Grant if (chan) *chan = c; 381987e5972SCameron Grant if (u < 0) return NULL; 382987e5972SCameron Grant 38333dbf14aSCameron Grant sc = devclass_get_softc(pcm_devclass, u); 38433dbf14aSCameron Grant if (sc == NULL || sc->magic == 0) return NULL; 38533dbf14aSCameron Grant 386987e5972SCameron Grant switch(d) { 387987e5972SCameron Grant case SND_DEV_CTL: /* /dev/mixer handled by pcm */ 388987e5972SCameron Grant case SND_DEV_STATUS: /* /dev/sndstat handled by pcm */ 389987e5972SCameron Grant case SND_DEV_DSP: 390987e5972SCameron Grant case SND_DEV_DSP16: 391987e5972SCameron Grant case SND_DEV_AUDIO: 39233dbf14aSCameron Grant return sc; 393987e5972SCameron Grant 394987e5972SCameron Grant case SND_DEV_SEQ: /* XXX when enabled... */ 395987e5972SCameron Grant case SND_DEV_SEQ2: 396987e5972SCameron Grant case SND_DEV_MIDIN: 397987e5972SCameron Grant case SND_DEV_SNDPROC: /* /dev/sndproc handled by pcm */ 398987e5972SCameron Grant default: 399987e5972SCameron Grant printf("unsupported subdevice %d\n", d); 400987e5972SCameron Grant return NULL; 401987e5972SCameron Grant } 402987e5972SCameron Grant } 403987e5972SCameron Grant 404987e5972SCameron Grant static int 405987e5972SCameron Grant sndopen(dev_t i_dev, int flags, int mode, struct proc *p) 406987e5972SCameron Grant { 407987e5972SCameron Grant int dev, unit, chan; 408987e5972SCameron Grant snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 409987e5972SCameron Grant 410987e5972SCameron Grant DEB(printf("open snd%d subdev %d flags 0x%08x mode 0x%08x\n", 411987e5972SCameron Grant unit, dev, flags, mode)); 412987e5972SCameron Grant 413987e5972SCameron Grant switch(dev) { 414987e5972SCameron Grant case SND_DEV_STATUS: 415987e5972SCameron Grant if (status_isopen) return EBUSY; 416987e5972SCameron Grant status_isopen = 1; 417987e5972SCameron Grant return 0; 418987e5972SCameron Grant 419987e5972SCameron Grant case SND_DEV_CTL: 42033dbf14aSCameron Grant return d? mixer_busy(d, 1) : ENXIO; 421987e5972SCameron Grant 422987e5972SCameron Grant case SND_DEV_AUDIO: 423987e5972SCameron Grant case SND_DEV_DSP: 424987e5972SCameron Grant case SND_DEV_DSP16: 4255b78a734SCameron Grant case SND_DEV_NORESET: 426987e5972SCameron Grant return d? dsp_open(d, chan, flags, dev) : ENXIO; 427987e5972SCameron Grant 428987e5972SCameron Grant default: 429987e5972SCameron Grant return ENXIO; 430987e5972SCameron Grant } 431987e5972SCameron Grant } 432987e5972SCameron Grant 433987e5972SCameron Grant static int 434987e5972SCameron Grant sndclose(dev_t i_dev, int flags, int mode, struct proc *p) 435987e5972SCameron Grant { 436987e5972SCameron Grant int dev, unit, chan; 437987e5972SCameron Grant snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 438987e5972SCameron Grant 439987e5972SCameron Grant DEB(printf("close snd%d subdev %d\n", unit, dev)); 440987e5972SCameron Grant 441987e5972SCameron Grant switch(dev) { /* only those for which close makes sense */ 442987e5972SCameron Grant case SND_DEV_STATUS: 443987e5972SCameron Grant if (!status_isopen) return EBADF; 444987e5972SCameron Grant status_isopen = 0; 445987e5972SCameron Grant return 0; 446987e5972SCameron Grant 447987e5972SCameron Grant case SND_DEV_CTL: 44833dbf14aSCameron Grant return d? mixer_busy(d, 0) : ENXIO; 449987e5972SCameron Grant 450987e5972SCameron Grant case SND_DEV_AUDIO: 451987e5972SCameron Grant case SND_DEV_DSP: 452987e5972SCameron Grant case SND_DEV_DSP16: 453987e5972SCameron Grant return d? dsp_close(d, chan, dev) : ENXIO; 454987e5972SCameron Grant 455987e5972SCameron Grant default: 456987e5972SCameron Grant return ENXIO; 457987e5972SCameron Grant } 458987e5972SCameron Grant } 459987e5972SCameron Grant 460987e5972SCameron Grant static int 461987e5972SCameron Grant sndread(dev_t i_dev, struct uio *buf, int flag) 462987e5972SCameron Grant { 463987e5972SCameron Grant int dev, unit, chan; 464987e5972SCameron Grant snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 465987e5972SCameron Grant DEB(printf("read snd%d subdev %d flag 0x%08x\n", unit, dev, flag)); 466987e5972SCameron Grant 467987e5972SCameron Grant switch(dev) { 468987e5972SCameron Grant case SND_DEV_STATUS: 469987e5972SCameron Grant return status_isopen? status_read(buf) : EBADF; 470987e5972SCameron Grant 471987e5972SCameron Grant case SND_DEV_AUDIO: 472987e5972SCameron Grant case SND_DEV_DSP: 473987e5972SCameron Grant case SND_DEV_DSP16: 474987e5972SCameron Grant return d? dsp_read(d, chan, buf, flag) : EBADF; 475987e5972SCameron Grant 476987e5972SCameron Grant default: 477987e5972SCameron Grant return ENXIO; 478987e5972SCameron Grant } 479987e5972SCameron Grant } 480987e5972SCameron Grant 481987e5972SCameron Grant static int 482987e5972SCameron Grant sndwrite(dev_t i_dev, struct uio *buf, int flag) 483987e5972SCameron Grant { 484987e5972SCameron Grant int dev, unit, chan; 485987e5972SCameron Grant snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 486987e5972SCameron Grant 487987e5972SCameron Grant DEB(printf("write snd%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag)); 488987e5972SCameron Grant 489987e5972SCameron Grant switch(dev) { /* only writeable devices */ 490987e5972SCameron Grant case SND_DEV_DSP: 491987e5972SCameron Grant case SND_DEV_DSP16: 492987e5972SCameron Grant case SND_DEV_AUDIO: 493987e5972SCameron Grant return d? dsp_write(d, chan, buf, flag) : EBADF; 494987e5972SCameron Grant 495987e5972SCameron Grant default: 496987e5972SCameron Grant return EPERM; /* for non-writeable devices ; */ 497987e5972SCameron Grant } 498987e5972SCameron Grant } 499987e5972SCameron Grant 500987e5972SCameron Grant static int 501987e5972SCameron Grant sndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p) 502987e5972SCameron Grant { 503987e5972SCameron Grant int dev, chan; 504987e5972SCameron Grant snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan); 505987e5972SCameron Grant 506987e5972SCameron Grant if (d == NULL) return ENXIO; 507987e5972SCameron Grant 508987e5972SCameron Grant switch(dev) { 509987e5972SCameron Grant case SND_DEV_CTL: 510987e5972SCameron Grant return mixer_ioctl(d, cmd, arg); 511987e5972SCameron Grant 512987e5972SCameron Grant case SND_DEV_AUDIO: 513987e5972SCameron Grant case SND_DEV_DSP: 514987e5972SCameron Grant case SND_DEV_DSP16: 5151ad869dbSCameron Grant if (IOCGROUP(cmd) == 'M') 5161ad869dbSCameron Grant return mixer_ioctl(d, cmd, arg); 5171ad869dbSCameron Grant else 518987e5972SCameron Grant return dsp_ioctl(d, chan, cmd, arg); 519987e5972SCameron Grant 520987e5972SCameron Grant default: 521987e5972SCameron Grant return ENXIO; 522987e5972SCameron Grant } 523987e5972SCameron Grant } 524987e5972SCameron Grant 525987e5972SCameron Grant static int 526987e5972SCameron Grant sndpoll(dev_t i_dev, int events, struct proc *p) 527987e5972SCameron Grant { 528987e5972SCameron Grant int dev, chan; 529987e5972SCameron Grant snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan); 530987e5972SCameron Grant 5310e25481fSCameron Grant DEB(printf("sndpoll d 0x%p dev 0x%04x events 0x%08x\n", d, dev, events)); 532987e5972SCameron Grant 533987e5972SCameron Grant if (d == NULL) return ENXIO; 534987e5972SCameron Grant 535987e5972SCameron Grant switch(dev) { 536987e5972SCameron Grant case SND_DEV_AUDIO: 537987e5972SCameron Grant case SND_DEV_DSP: 538987e5972SCameron Grant case SND_DEV_DSP16: 539987e5972SCameron Grant return dsp_poll(d, chan, events, p); 540987e5972SCameron Grant 541987e5972SCameron Grant default: 542987e5972SCameron Grant return (events & 543987e5972SCameron Grant (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)) | POLLHUP; 544987e5972SCameron Grant } 545987e5972SCameron Grant } 546987e5972SCameron Grant 547987e5972SCameron Grant /* 548987e5972SCameron Grant * The mmap interface allows access to the play and read buffer, 549987e5972SCameron Grant * plus the device descriptor. 550987e5972SCameron Grant * The various blocks are accessible at the following offsets: 551987e5972SCameron Grant * 552987e5972SCameron Grant * 0x00000000 ( 0 ) : write buffer ; 553987e5972SCameron Grant * 0x01000000 (16 MB) : read buffer ; 554987e5972SCameron Grant * 0x02000000 (32 MB) : device descriptor (dangerous!) 555987e5972SCameron Grant * 556987e5972SCameron Grant * WARNING: the mmap routines assume memory areas are aligned. This 557987e5972SCameron Grant * is true (probably) for the dma buffers, but likely false for the 558987e5972SCameron Grant * device descriptor. As a consequence, we do not know where it is 559987e5972SCameron Grant * located in the requested area. 560987e5972SCameron Grant */ 561987e5972SCameron Grant static int 562987e5972SCameron Grant sndmmap(dev_t i_dev, vm_offset_t offset, int nprot) 563987e5972SCameron Grant { 564987e5972SCameron Grant int unit, dev, chan; 565987e5972SCameron Grant snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 566987e5972SCameron Grant 567987e5972SCameron Grant DEB(printf("sndmmap d 0x%p dev 0x%04x ofs 0x%08x nprot 0x%08x\n", 568987e5972SCameron Grant d, dev, offset, nprot)); 569987e5972SCameron Grant 570987e5972SCameron Grant if (d == NULL || nprot & PROT_EXEC) return -1; /* forbidden */ 571987e5972SCameron Grant 572987e5972SCameron Grant switch(dev) { 573987e5972SCameron Grant case SND_DEV_AUDIO: 574987e5972SCameron Grant case SND_DEV_DSP: 575987e5972SCameron Grant case SND_DEV_DSP16: 576987e5972SCameron Grant return dsp_mmap(d, chan, offset, nprot); 577987e5972SCameron Grant 578987e5972SCameron Grant default: 579987e5972SCameron Grant return -1; 580987e5972SCameron Grant } 581987e5972SCameron Grant } 582987e5972SCameron Grant 583987e5972SCameron Grant static int 584987e5972SCameron Grant status_init(char *buf, int size) 585987e5972SCameron Grant { 586987e5972SCameron Grant int i; 587987e5972SCameron Grant device_t dev; 588987e5972SCameron Grant snddev_info *d; 589987e5972SCameron Grant 590987e5972SCameron Grant snprintf(buf, size, "FreeBSD Audio Driver (newpcm) %s %s\n" 591987e5972SCameron Grant "Installed devices:\n", __DATE__, __TIME__); 592987e5972SCameron Grant 593987e5972SCameron Grant for (i = 0; i <= devclass_get_maxunit(pcm_devclass); i++) { 59433dbf14aSCameron Grant d = devclass_get_softc(pcm_devclass, i); 595987e5972SCameron Grant if (!d) continue; 596987e5972SCameron Grant dev = devclass_get_device(pcm_devclass, i); 597bf8ca271SCameron Grant if (1) { 598bf8ca271SCameron Grant snprintf(buf + strlen(buf), size - strlen(buf), 599bf8ca271SCameron Grant "pcm%d: <%s> %s", 600bf8ca271SCameron Grant i, device_get_desc(dev), d->status); 601bf8ca271SCameron Grant if (d->chancount > 0) 602bf8ca271SCameron Grant snprintf(buf + strlen(buf), size - strlen(buf), 603bf8ca271SCameron Grant " (%dp/%dr channels%s)\n", 604987e5972SCameron Grant d->playcount, d->reccount, 605987e5972SCameron Grant (!(d->flags & SD_F_SIMPLEX))? " duplex" : ""); 606bf8ca271SCameron Grant else 607bf8ca271SCameron Grant snprintf(buf + strlen(buf), size - strlen(buf), 6089bc50208SCameron Grant " (mixer only)\n"); 609bf8ca271SCameron Grant } 610987e5972SCameron Grant } 611987e5972SCameron Grant return strlen(buf); 612987e5972SCameron Grant } 613987e5972SCameron Grant 614987e5972SCameron Grant static int 615987e5972SCameron Grant status_read(struct uio *buf) 616987e5972SCameron Grant { 617987e5972SCameron Grant static char status_buf[4096]; 618987e5972SCameron Grant static int bufptr = 0, buflen = 0; 619987e5972SCameron Grant int l; 620987e5972SCameron Grant 621987e5972SCameron Grant if (status_isopen == 1) { 622987e5972SCameron Grant status_isopen++; 623987e5972SCameron Grant bufptr = 0; 624987e5972SCameron Grant buflen = status_init(status_buf, sizeof status_buf); 625987e5972SCameron Grant } 626987e5972SCameron Grant 627987e5972SCameron Grant l = min(buf->uio_resid, buflen - bufptr); 628987e5972SCameron Grant bufptr += l; 629987e5972SCameron Grant return (l > 0)? uiomove(status_buf + bufptr - l, l, buf) : 0; 630987e5972SCameron Grant } 63133dbf14aSCameron Grant 63233dbf14aSCameron Grant static int 63333dbf14aSCameron Grant sndpcm_modevent(module_t mod, int type, void *data) 63433dbf14aSCameron Grant { 63533dbf14aSCameron Grant 63633dbf14aSCameron Grant switch (type) { 63733dbf14aSCameron Grant case MOD_LOAD: 63833dbf14aSCameron Grant break; 63933dbf14aSCameron Grant case MOD_UNLOAD: 640c9b53085SCameron Grant if (status_isopen) 641c9b53085SCameron Grant return EBUSY; 64233dbf14aSCameron Grant if (status_dev) 64333dbf14aSCameron Grant destroy_dev(status_dev); 64433dbf14aSCameron Grant status_dev = 0; 64533dbf14aSCameron Grant break; 64633dbf14aSCameron Grant default: 64733dbf14aSCameron Grant break; 64833dbf14aSCameron Grant } 64933dbf14aSCameron Grant return 0; 65033dbf14aSCameron Grant } 65133dbf14aSCameron Grant 65233dbf14aSCameron Grant static moduledata_t sndpcm_mod = { 65333dbf14aSCameron Grant "snd_pcm", 65433dbf14aSCameron Grant sndpcm_modevent, 65533dbf14aSCameron Grant NULL 65633dbf14aSCameron Grant }; 65733dbf14aSCameron Grant DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 65833dbf14aSCameron Grant MODULE_VERSION(snd_pcm, PCM_MODVER); 659