sound.c revision 65340
150724Scg/* 250724Scg * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> 350724Scg * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it) 450724Scg * All rights reserved. 550724Scg * 650724Scg * Redistribution and use in source and binary forms, with or without 750724Scg * modification, are permitted provided that the following conditions 850724Scg * are met: 950724Scg * 1. Redistributions of source code must retain the above copyright 1050724Scg * notice, this list of conditions and the following disclaimer. 1150724Scg * 2. Redistributions in binary form must reproduce the above copyright 1250724Scg * notice, this list of conditions and the following disclaimer in the 1350724Scg * documentation and/or other materials provided with the distribution. 1450724Scg * 1550724Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1650724Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1750724Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1850724Scg * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1950724Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2050724Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2150724Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2250724Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2350724Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2450724Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2550724Scg * SUCH DAMAGE. 2650724Scg * 2750733Speter * $FreeBSD: head/sys/dev/sound/pcm/sound.c 65340 2000-09-01 20:09:24Z cg $ 2850724Scg */ 2950724Scg 3053465Scg#include <dev/sound/pcm/sound.h> 3165207Scg#include <sys/sysctl.h> 3265207Scg#include "opt_devfs.h" 3350724Scg 3465340Scgstatic dev_t status_dev = 0; 3550724Scgstatic int status_isopen = 0; 3650724Scgstatic int status_init(char *buf, int size); 3750724Scgstatic int status_read(struct uio *buf); 3850724Scg 3950724Scgstatic d_open_t sndopen; 4050724Scgstatic d_close_t sndclose; 4150724Scgstatic d_ioctl_t sndioctl; 4250724Scgstatic d_read_t sndread; 4350724Scgstatic d_write_t sndwrite; 4450724Scgstatic d_mmap_t sndmmap; 4550724Scgstatic d_poll_t sndpoll; 4650724Scg 4750724Scg#define CDEV_MAJOR 30 4850724Scgstatic struct cdevsw snd_cdevsw = { 4950724Scg /* open */ sndopen, 5050724Scg /* close */ sndclose, 5150724Scg /* read */ sndread, 5250724Scg /* write */ sndwrite, 5350724Scg /* ioctl */ sndioctl, 5450724Scg /* poll */ sndpoll, 5550724Scg /* mmap */ sndmmap, 5650724Scg /* strategy */ nostrategy, 5750724Scg /* name */ "snd", 5850724Scg /* maj */ CDEV_MAJOR, 5950724Scg /* dump */ nodump, 6050724Scg /* psize */ nopsize, 6150724Scg /* flags */ 0, 6250724Scg /* bmaj */ -1 6350724Scg}; 6450724Scg 6559660Scg/* 6659660ScgPROPOSAL: 6750724Scgeach unit needs: 6850724Scgstatus, mixer, dsp, dspW, audio, sequencer, midi-in, seq2, sndproc = 9 devices 6950724ScgdspW and audio are deprecated. 7050724Scgdsp needs min 64 channels, will give it 256 7150724Scg 7259660Scgminor = (unit << 20) + (dev << 16) + channel 7359660Scgcurrently minor = (channel << 16) + (unit << 4) + dev 7450724Scg 7550724Scgnomenclature: 7650724Scg /dev/pcmX/dsp.(0..255) 7750724Scg /dev/pcmX/dspW 7850724Scg /dev/pcmX/audio 7950724Scg /dev/pcmX/status 8050724Scg /dev/pcmX/mixer 8150724Scg [etc.] 8250724Scg*/ 8350724Scg 8450724Scg#define PCMMINOR(x) (minor(x)) 8559665Scg#define PCMCHAN(x) ((PCMMINOR(x) & 0x00ff0000) >> 16) 8650724Scg#define PCMUNIT(x) ((PCMMINOR(x) & 0x000000f0) >> 4) 8750724Scg#define PCMDEV(x) (PCMMINOR(x) & 0x0000000f) 8859660Scg#define PCMMKMINOR(u, d, c) ((((c) & 0xff) << 16) | (((u) & 0x0f) << 4) | ((d) & 0x0f)) 8950724Scg 9050724Scgstatic devclass_t pcm_devclass; 9165207Scg#ifdef DEVFS 9265207Scgint snd_unit; 9365207ScgTUNABLE_INT_DECL("hw.sndunit", 0, snd_unit); 9465207Scg#endif 9550724Scg 9665340Scg#ifdef DEVFS 9765340Scgstatic void 9865340Scgpcm_makelinks(void *dummy) 9950724Scg{ 10065340Scg int unit; 10165340Scg dev_t pdev; 10265340Scg static dev_t dsp = 0, dspW = 0, audio = 0, mixer = 0; 10365340Scg 10465340Scg if (pcm_devclass == NULL) 10565340Scg return; 10665340Scg if (dsp) { 10765340Scg destroy_dev(dsp); 10865340Scg dsp = 0; 10965340Scg } 11065340Scg if (dspW) { 11165340Scg destroy_dev(dspW); 11265340Scg dspW = 0; 11365340Scg } 11465340Scg if (audio) { 11565340Scg destroy_dev(audio); 11665340Scg audio = 0; 11765340Scg } 11865340Scg if (mixer) { 11965340Scg destroy_dev(mixer); 12065340Scg mixer = 0; 12165340Scg } 12265340Scg 12365340Scg unit = snd_unit; 12465340Scg if (unit < 0 || unit > devclass_get_maxunit(pcm_devclass)) 12565340Scg return; 12665340Scg if (devclass_get_softc(pcm_devclass, unit) == NULL) 12765340Scg return; 12865340Scg 12965340Scg pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, 0)); 13065340Scg dsp = make_dev_alias(pdev, "dsp"); 13165340Scg pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, 0)); 13265340Scg dspW = make_dev_alias(pdev, "dspW"); 13365340Scg pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, 0)); 13465340Scg audio = make_dev_alias(pdev, "audio"); 13565340Scg pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_CTL, 0)); 13665340Scg mixer = make_dev_alias(pdev, "mixer"); 13750724Scg} 13850724Scg 13965340Scgstatic int 14065340Scgsysctl_hw_sndunit(SYSCTL_HANDLER_ARGS) 14165340Scg{ 14265340Scg int error, unit; 14365340Scg 14465340Scg unit = snd_unit; 14565340Scg error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); 14665340Scg if (error == 0 && req->newptr != NULL) { 14765340Scg snd_unit = unit; 14865340Scg pcm_makelinks(NULL); 14965340Scg } 15065340Scg return (error); 15165340Scg} 15265340ScgSYSCTL_PROC(_hw, OID_AUTO, sndunit, CTLTYPE_INT | CTLFLAG_RW, 15365340Scg 0, sizeof(int), sysctl_hw_sndunit, "I", ""); 15465340Scg#endif 15565340Scg 15650724Scgint 15750724Scgpcm_addchan(device_t dev, int dir, pcm_channel *templ, void *devinfo) 15850724Scg{ 15965340Scg int unit = device_get_unit(dev), idx; 16050724Scg snddev_info *d = device_get_softc(dev); 16165340Scg pcm_channel *chns, *ch; 16265340Scg char *dirs; 16350724Scg 16465340Scg dirs = ((dir == PCMDIR_PLAY)? "play" : "record"); 16565340Scg chns = ((dir == PCMDIR_PLAY)? d->play : d->rec); 16665340Scg idx = ((dir == PCMDIR_PLAY)? d->playcount++ : d->reccount++); 16765340Scg 16865340Scg if (chns == NULL) { 16965340Scg device_printf(dev, "bad channel add (%s:%d)\n", dirs, idx); 17061344Scg return 1; 17161344Scg } 17265340Scg ch = &chns[idx]; 17350724Scg *ch = *templ; 17461886Scg ch->parent = d; 17555483Scg if (chn_init(ch, devinfo, dir)) { 17665340Scg device_printf(dev, "chn_init() for (%s:%d) failed\n", dirs, idx); 17755483Scg return 1; 17855483Scg } 17954826Scg make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount), 18054826Scg UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", unit, d->chancount); 18165340Scg make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount), 18265340Scg UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", unit, d->chancount); 18354826Scg make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount), 18454826Scg UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", unit, d->chancount); 18554826Scg /* XXX SND_DEV_NORESET? */ 18650724Scg d->chancount++; 18765340Scg#ifdef DEVFS 18865340Scg if (d->chancount == d->maxchans) 18965340Scg pcm_makelinks(NULL); 19065340Scg#endif 19150724Scg return 0; 19250724Scg} 19350724Scg 19465340Scgstatic int 19565340Scgpcm_killchan(device_t dev, int dir) 19665340Scg{ 19765340Scg int unit = device_get_unit(dev), idx; 19865340Scg snddev_info *d = device_get_softc(dev); 19965340Scg pcm_channel *chns, *ch; 20065340Scg char *dirs; 20165340Scg dev_t pdev; 20265340Scg 20365340Scg dirs = ((dir == PCMDIR_PLAY)? "play" : "record"); 20465340Scg chns = ((dir == PCMDIR_PLAY)? d->play : d->rec); 20565340Scg idx = ((dir == PCMDIR_PLAY)? --d->playcount : --d->reccount); 20665340Scg 20765340Scg if (chns == NULL || idx < 0) { 20865340Scg device_printf(dev, "bad channel kill (%s:%d)\n", dirs, idx); 20965340Scg return 1; 21065340Scg } 21165340Scg ch = &chns[idx]; 21265340Scg if (chn_kill(ch)) { 21365340Scg device_printf(dev, "chn_kill() for (%s:%d) failed\n", dirs, idx); 21465340Scg return 1; 21565340Scg } 21665340Scg d->chancount--; 21765340Scg pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount)); 21865340Scg destroy_dev(pdev); 21965340Scg pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount)); 22065340Scg destroy_dev(pdev); 22165340Scg pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount)); 22265340Scg destroy_dev(pdev); 22365340Scg return 0; 22465340Scg} 22565340Scg 22650724Scgint 22750724Scgpcm_setstatus(device_t dev, char *str) 22850724Scg{ 22950724Scg snddev_info *d = device_get_softc(dev); 23050724Scg strncpy(d->status, str, SND_STATUSLEN); 23150724Scg return 0; 23250724Scg} 23350724Scg 23450724Scgu_int32_t 23550724Scgpcm_getflags(device_t dev) 23650724Scg{ 23750724Scg snddev_info *d = device_get_softc(dev); 23850724Scg return d->flags; 23950724Scg} 24050724Scg 24150724Scgvoid 24250724Scgpcm_setflags(device_t dev, u_int32_t val) 24350724Scg{ 24450724Scg snddev_info *d = device_get_softc(dev); 24550724Scg d->flags = val; 24650724Scg} 24750724Scg 24858384Scgvoid * 24958384Scgpcm_getdevinfo(device_t dev) 25058384Scg{ 25158384Scg snddev_info *d = device_get_softc(dev); 25258384Scg return d->devinfo; 25358384Scg} 25458384Scg 25554460Scgvoid 25654460Scgpcm_setswap(device_t dev, pcm_swap_t *swap) 25754460Scg{ 25854460Scg snddev_info *d = device_get_softc(dev); 25954460Scg d->swap = swap; 26054460Scg} 26165340Scg 26250724Scg/* This is the generic init routine */ 26350724Scgint 26450724Scgpcm_register(device_t dev, void *devinfo, int numplay, int numrec) 26550724Scg{ 26650724Scg int sz, unit = device_get_unit(dev); 26750724Scg snddev_info *d = device_get_softc(dev); 26850724Scg 26950724Scg if (!pcm_devclass) { 27050724Scg pcm_devclass = device_get_devclass(dev); 27165340Scg status_dev = make_dev(&snd_cdevsw, PCMMKMINOR(0, SND_DEV_STATUS, 0), 27252755Stanimura UID_ROOT, GID_WHEEL, 0444, "sndstat"); 27352823Stanimura } 27454826Scg make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0), 27552823Stanimura UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit); 27661886Scg d->dev = dev; 27750724Scg d->devinfo = devinfo; 27850724Scg d->chancount = d->playcount = d->reccount = 0; 27965340Scg d->maxchans = numplay + numrec; 28050724Scg sz = (numplay + numrec) * sizeof(pcm_channel *); 28150724Scg 28255494Scg if (sz > 0) { 28355494Scg d->aplay = (pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT); 28455494Scg if (!d->aplay) goto no; 28555494Scg bzero(d->aplay, sz); 28650724Scg 28755494Scg d->arec = (pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT); 28855494Scg if (!d->arec) goto no; 28955494Scg bzero(d->arec, sz); 29059021Scg 29159021Scg sz = (numplay + numrec) * sizeof(int); 29259021Scg d->ref = (int *)malloc(sz, M_DEVBUF, M_NOWAIT); 29359021Scg if (!d->ref) goto no; 29459021Scg bzero(d->ref, sz); 29555494Scg } 29655494Scg 29755494Scg if (numplay > 0) { 29855494Scg d->play = (pcm_channel *)malloc(numplay * sizeof(pcm_channel), 29955494Scg M_DEVBUF, M_NOWAIT); 30055494Scg if (!d->play) goto no; 30155494Scg bzero(d->play, numplay * sizeof(pcm_channel)); 30261344Scg } else 30361344Scg d->play = NULL; 30455494Scg 30555494Scg if (numrec > 0) { 30655494Scg d->rec = (pcm_channel *)malloc(numrec * sizeof(pcm_channel), 30755494Scg M_DEVBUF, M_NOWAIT); 30855494Scg if (!d->rec) goto no; 30955494Scg bzero(d->rec, numrec * sizeof(pcm_channel)); 31061344Scg } else 31161344Scg d->rec = NULL; 31255494Scg 31361144Scg if (numplay == 0 || numrec == 0) 31461144Scg d->flags |= SD_F_SIMPLEX; 31561144Scg 31650724Scg fkchan_setup(&d->fakechan); 31750724Scg chn_init(&d->fakechan, NULL, 0); 31850724Scg d->magic = MAGIC(unit); /* debugging... */ 31954460Scg d->swap = NULL; 32050724Scg 32150724Scg return 0; 32250724Scgno: 32350724Scg if (d->aplay) free(d->aplay, M_DEVBUF); 32450724Scg if (d->play) free(d->play, M_DEVBUF); 32550724Scg if (d->arec) free(d->arec, M_DEVBUF); 32650724Scg if (d->rec) free(d->rec, M_DEVBUF); 32765340Scg if (d->ref) free(d->ref, M_DEVBUF); 32850724Scg return ENXIO; 32950724Scg} 33050724Scg 33165340Scgint 33265340Scgpcm_unregister(device_t dev) 33365207Scg{ 33465340Scg int r, i, unit = device_get_unit(dev); 33565340Scg snddev_info *d = device_get_softc(dev); 33665207Scg dev_t pdev; 33765207Scg 33865340Scg r = 0; 33965340Scg for (i = 0; i < d->chancount; i++) 34065340Scg if (d->ref[i]) r = EBUSY; 34165340Scg if (r) return r; 34265340Scg if (mixer_isbusy(d) || status_isopen) return EBUSY; 34365207Scg 34465340Scg pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_CTL, 0)); 34565340Scg destroy_dev(pdev); 34665340Scg mixer_uninit(dev); 34765207Scg 34865340Scg while (d->playcount > 0) 34965340Scg pcm_killchan(dev, PCMDIR_PLAY); 35065340Scg while (d->reccount > 0) 35165340Scg pcm_killchan(dev, PCMDIR_REC); 35265340Scg d->magic = 0; 35365207Scg 35465340Scg if (d->aplay) free(d->aplay, M_DEVBUF); 35565340Scg if (d->play) free(d->play, M_DEVBUF); 35665340Scg if (d->arec) free(d->arec, M_DEVBUF); 35765340Scg if (d->rec) free(d->rec, M_DEVBUF); 35865340Scg if (d->ref) free(d->ref, M_DEVBUF); 35965207Scg 36065340Scg#ifdef DEVFS 36165340Scg pcm_makelinks(NULL); 36265340Scg#endif 36365340Scg return 0; 36465207Scg} 36565207Scg 36650724Scg/* 36750724Scg * a small utility function which, given a device number, returns 36850724Scg * a pointer to the associated snddev_info struct, and sets the unit 36950724Scg * number. 37050724Scg */ 37150724Scgstatic snddev_info * 37250724Scgget_snddev_info(dev_t i_dev, int *unit, int *dev, int *chan) 37350724Scg{ 37465340Scg snddev_info *sc; 37550724Scg int u, d, c; 37650724Scg 37750724Scg u = PCMUNIT(i_dev); 37850724Scg d = PCMDEV(i_dev); 37950724Scg c = PCMCHAN(i_dev); 38050724Scg if (u > devclass_get_maxunit(pcm_devclass)) u = -1; 38150724Scg if (unit) *unit = u; 38250724Scg if (dev) *dev = d; 38350724Scg if (chan) *chan = c; 38450724Scg if (u < 0) return NULL; 38550724Scg 38665340Scg sc = devclass_get_softc(pcm_devclass, u); 38765340Scg if (sc == NULL || sc->magic == 0) return NULL; 38865340Scg 38965340Scg switch(d) { 39050724Scg case SND_DEV_CTL: /* /dev/mixer handled by pcm */ 39150724Scg case SND_DEV_STATUS: /* /dev/sndstat handled by pcm */ 39250724Scg case SND_DEV_DSP: 39350724Scg case SND_DEV_DSP16: 39450724Scg case SND_DEV_AUDIO: 39565340Scg return sc; 39650724Scg 39750724Scg case SND_DEV_SEQ: /* XXX when enabled... */ 39850724Scg case SND_DEV_SEQ2: 39950724Scg case SND_DEV_MIDIN: 40050724Scg case SND_DEV_SNDPROC: /* /dev/sndproc handled by pcm */ 40150724Scg default: 40250724Scg printf("unsupported subdevice %d\n", d); 40350724Scg return NULL; 40450724Scg } 40550724Scg} 40650724Scg 40750724Scgstatic int 40850724Scgsndopen(dev_t i_dev, int flags, int mode, struct proc *p) 40950724Scg{ 41050724Scg int dev, unit, chan; 41150724Scg snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 41250724Scg 41350724Scg DEB(printf("open snd%d subdev %d flags 0x%08x mode 0x%08x\n", 41450724Scg unit, dev, flags, mode)); 41550724Scg 41650724Scg switch(dev) { 41750724Scg case SND_DEV_STATUS: 41850724Scg if (status_isopen) return EBUSY; 41950724Scg status_isopen = 1; 42050724Scg return 0; 42150724Scg 42250724Scg case SND_DEV_CTL: 42365340Scg return d? mixer_busy(d, 1) : ENXIO; 42450724Scg 42550724Scg case SND_DEV_AUDIO: 42650724Scg case SND_DEV_DSP: 42750724Scg case SND_DEV_DSP16: 42851769Scg case SND_DEV_NORESET: 42950724Scg return d? dsp_open(d, chan, flags, dev) : ENXIO; 43050724Scg 43150724Scg default: 43250724Scg return ENXIO; 43350724Scg } 43450724Scg} 43550724Scg 43650724Scgstatic int 43750724Scgsndclose(dev_t i_dev, int flags, int mode, struct proc *p) 43850724Scg{ 43950724Scg int dev, unit, chan; 44050724Scg snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 44150724Scg 44250724Scg DEB(printf("close snd%d subdev %d\n", unit, dev)); 44350724Scg 44450724Scg switch(dev) { /* only those for which close makes sense */ 44550724Scg case SND_DEV_STATUS: 44650724Scg if (!status_isopen) return EBADF; 44750724Scg status_isopen = 0; 44850724Scg return 0; 44950724Scg 45050724Scg case SND_DEV_CTL: 45165340Scg return d? mixer_busy(d, 0) : ENXIO; 45250724Scg 45350724Scg case SND_DEV_AUDIO: 45450724Scg case SND_DEV_DSP: 45550724Scg case SND_DEV_DSP16: 45650724Scg return d? dsp_close(d, chan, dev) : ENXIO; 45750724Scg 45850724Scg default: 45950724Scg return ENXIO; 46050724Scg } 46150724Scg} 46250724Scg 46350724Scgstatic int 46450724Scgsndread(dev_t i_dev, struct uio *buf, int flag) 46550724Scg{ 46650724Scg int dev, unit, chan; 46750724Scg snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 46850724Scg DEB(printf("read snd%d subdev %d flag 0x%08x\n", unit, dev, flag)); 46950724Scg 47050724Scg switch(dev) { 47150724Scg case SND_DEV_STATUS: 47250724Scg return status_isopen? status_read(buf) : EBADF; 47350724Scg 47450724Scg case SND_DEV_AUDIO: 47550724Scg case SND_DEV_DSP: 47650724Scg case SND_DEV_DSP16: 47750724Scg return d? dsp_read(d, chan, buf, flag) : EBADF; 47850724Scg 47950724Scg default: 48050724Scg return ENXIO; 48150724Scg } 48250724Scg} 48350724Scg 48450724Scgstatic int 48550724Scgsndwrite(dev_t i_dev, struct uio *buf, int flag) 48650724Scg{ 48750724Scg int dev, unit, chan; 48850724Scg snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 48950724Scg 49050724Scg DEB(printf("write snd%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag)); 49150724Scg 49250724Scg switch(dev) { /* only writeable devices */ 49350724Scg case SND_DEV_DSP: 49450724Scg case SND_DEV_DSP16: 49550724Scg case SND_DEV_AUDIO: 49650724Scg return d? dsp_write(d, chan, buf, flag) : EBADF; 49750724Scg 49850724Scg default: 49950724Scg return EPERM; /* for non-writeable devices ; */ 50050724Scg } 50150724Scg} 50250724Scg 50350724Scgstatic int 50450724Scgsndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p) 50550724Scg{ 50650724Scg int dev, chan; 50750724Scg snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan); 50850724Scg 50950724Scg if (d == NULL) return ENXIO; 51050724Scg 51150724Scg switch(dev) { 51250724Scg case SND_DEV_CTL: 51350724Scg return mixer_ioctl(d, cmd, arg); 51450724Scg 51550724Scg case SND_DEV_AUDIO: 51650724Scg case SND_DEV_DSP: 51750724Scg case SND_DEV_DSP16: 51854857Scg if (IOCGROUP(cmd) == 'M') 51954857Scg return mixer_ioctl(d, cmd, arg); 52054857Scg else 52154857Scg return dsp_ioctl(d, chan, cmd, arg); 52250724Scg 52350724Scg default: 52450724Scg return ENXIO; 52550724Scg } 52650724Scg} 52750724Scg 52850724Scgstatic int 52950724Scgsndpoll(dev_t i_dev, int events, struct proc *p) 53050724Scg{ 53150724Scg int dev, chan; 53250724Scg snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan); 53350724Scg 53454155Scg DEB(printf("sndpoll d 0x%p dev 0x%04x events 0x%08x\n", d, dev, events)); 53550724Scg 53650724Scg if (d == NULL) return ENXIO; 53750724Scg 53850724Scg switch(dev) { 53950724Scg case SND_DEV_AUDIO: 54050724Scg case SND_DEV_DSP: 54150724Scg case SND_DEV_DSP16: 54250724Scg return dsp_poll(d, chan, events, p); 54350724Scg 54450724Scg default: 54550724Scg return (events & 54650724Scg (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)) | POLLHUP; 54750724Scg } 54850724Scg} 54950724Scg 55050724Scg/* 55150724Scg * The mmap interface allows access to the play and read buffer, 55250724Scg * plus the device descriptor. 55350724Scg * The various blocks are accessible at the following offsets: 55450724Scg * 55550724Scg * 0x00000000 ( 0 ) : write buffer ; 55650724Scg * 0x01000000 (16 MB) : read buffer ; 55750724Scg * 0x02000000 (32 MB) : device descriptor (dangerous!) 55850724Scg * 55950724Scg * WARNING: the mmap routines assume memory areas are aligned. This 56050724Scg * is true (probably) for the dma buffers, but likely false for the 56150724Scg * device descriptor. As a consequence, we do not know where it is 56250724Scg * located in the requested area. 56350724Scg */ 56450724Scgstatic int 56550724Scgsndmmap(dev_t i_dev, vm_offset_t offset, int nprot) 56650724Scg{ 56750724Scg int unit, dev, chan; 56850724Scg snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 56950724Scg 57050724Scg DEB(printf("sndmmap d 0x%p dev 0x%04x ofs 0x%08x nprot 0x%08x\n", 57150724Scg d, dev, offset, nprot)); 57250724Scg 57350724Scg if (d == NULL || nprot & PROT_EXEC) return -1; /* forbidden */ 57450724Scg 57550724Scg switch(dev) { 57650724Scg case SND_DEV_AUDIO: 57750724Scg case SND_DEV_DSP: 57850724Scg case SND_DEV_DSP16: 57950724Scg return dsp_mmap(d, chan, offset, nprot); 58050724Scg 58150724Scg default: 58250724Scg return -1; 58350724Scg } 58450724Scg} 58550724Scg 58650724Scgstatic int 58750724Scgstatus_init(char *buf, int size) 58850724Scg{ 58950724Scg int i; 59050724Scg device_t dev; 59150724Scg snddev_info *d; 59250724Scg 59350724Scg snprintf(buf, size, "FreeBSD Audio Driver (newpcm) %s %s\n" 59450724Scg "Installed devices:\n", __DATE__, __TIME__); 59550724Scg 59650724Scg for (i = 0; i <= devclass_get_maxunit(pcm_devclass); i++) { 59765340Scg d = devclass_get_softc(pcm_devclass, i); 59850724Scg if (!d) continue; 59950724Scg dev = devclass_get_device(pcm_devclass, i); 60055638Scg if (1) { 60155638Scg snprintf(buf + strlen(buf), size - strlen(buf), 60255638Scg "pcm%d: <%s> %s", 60355638Scg i, device_get_desc(dev), d->status); 60455638Scg if (d->chancount > 0) 60555638Scg snprintf(buf + strlen(buf), size - strlen(buf), 60655638Scg " (%dp/%dr channels%s)\n", 60755638Scg d->playcount, d->reccount, 60850724Scg (!(d->flags & SD_F_SIMPLEX))? " duplex" : ""); 60955638Scg else 61055638Scg snprintf(buf + strlen(buf), size - strlen(buf), 61156110Scg " (mixer only)\n"); 61255638Scg } 61350724Scg } 61450724Scg return strlen(buf); 61550724Scg} 61650724Scg 61750724Scgstatic int 61850724Scgstatus_read(struct uio *buf) 61950724Scg{ 62050724Scg static char status_buf[4096]; 62150724Scg static int bufptr = 0, buflen = 0; 62250724Scg int l; 62350724Scg 62450724Scg if (status_isopen == 1) { 62550724Scg status_isopen++; 62650724Scg bufptr = 0; 62750724Scg buflen = status_init(status_buf, sizeof status_buf); 62850724Scg } 62950724Scg 63050724Scg l = min(buf->uio_resid, buflen - bufptr); 63150724Scg bufptr += l; 63250724Scg return (l > 0)? uiomove(status_buf + bufptr - l, l, buf) : 0; 63350724Scg} 63465340Scg 63565340Scgstatic int 63665340Scgsndpcm_modevent(module_t mod, int type, void *data) 63765340Scg{ 63865340Scg 63965340Scg switch (type) { 64065340Scg case MOD_LOAD: 64165340Scg break; 64265340Scg case MOD_UNLOAD: 64365340Scg if (status_dev) 64465340Scg destroy_dev(status_dev); 64565340Scg status_dev = 0; 64665340Scg break; 64765340Scg default: 64865340Scg break; 64965340Scg } 65065340Scg return 0; 65165340Scg} 65265340Scg 65365340Scgstatic moduledata_t sndpcm_mod = { 65465340Scg "snd_pcm", 65565340Scg sndpcm_modevent, 65665340Scg NULL 65765340Scg}; 65865340ScgDECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 65965340ScgMODULE_VERSION(snd_pcm, PCM_MODVER); 660