sound.c revision 78670
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 78670 2001-06-23 17:36:51Z cg $ 2850724Scg */ 2950724Scg 3053465Scg#include <dev/sound/pcm/sound.h> 3177269Scg#include <dev/sound/pcm/vchan.h> 3265207Scg#include <sys/sysctl.h> 3350724Scg 3478362Scgdevclass_t pcm_devclass; 3550724Scg 3673127Scg#ifdef USING_DEVFS 3778362Scgint snd_unit = 0; 3877900SpeterTUNABLE_INT("hw.snd.unit", &snd_unit); 3973127Scg#endif 4050724Scg 4173127ScgSYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver"); 4273127Scg 4373131Scgvoid * 4473131Scgsnd_mtxcreate(const char *desc) 4573131Scg{ 4673131Scg#ifdef USING_MUTEX 4773131Scg struct mtx *m; 4873131Scg 4973131Scg m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); 5073131Scg if (m == NULL) 5173131Scg return NULL; 5273131Scg mtx_init(m, desc, MTX_RECURSE); 5373131Scg return m; 5473131Scg#else 5573757Scg return (void *)0xcafebabe; 5673131Scg#endif 5773131Scg} 5873131Scg 5973131Scgvoid 6073131Scgsnd_mtxfree(void *m) 6173131Scg{ 6273131Scg#ifdef USING_MUTEX 6373131Scg struct mtx *mtx = m; 6473131Scg 6573131Scg mtx_assert(mtx, MA_OWNED); 6673131Scg mtx_destroy(mtx); 6773131Scg free(mtx, M_DEVBUF); 6873131Scg#endif 6973131Scg} 7073131Scg 7173131Scgvoid 7273131Scgsnd_mtxassert(void *m) 7373131Scg{ 7473131Scg#ifdef USING_MUTEX 7578670Scg#ifdef INVARIANTS 7673131Scg struct mtx *mtx = m; 7773131Scg 7873131Scg mtx_assert(mtx, MA_OWNED); 7973131Scg#endif 8078670Scg#endif 8173131Scg} 8273131Scg 8373131Scgvoid 8473131Scgsnd_mtxlock(void *m) 8573131Scg{ 8673131Scg#ifdef USING_MUTEX 8773131Scg struct mtx *mtx = m; 8873131Scg 8973131Scg mtx_lock(mtx); 9073131Scg#endif 9173131Scg} 9273131Scg 9373131Scgvoid 9473131Scgsnd_mtxunlock(void *m) 9573131Scg{ 9673131Scg#ifdef USING_MUTEX 9773131Scg struct mtx *mtx = m; 9873131Scg 9973131Scg mtx_unlock(mtx); 10073131Scg#endif 10173131Scg} 10273131Scg 10373131Scgint 10473131Scgsnd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) 10573131Scg{ 10673131Scg#ifdef USING_MUTEX 10773131Scg flags &= INTR_MPSAFE; 10878366Speter flags |= INTR_TYPE_AV; 10973131Scg#else 11078366Speter flags = INTR_TYPE_AV; 11173131Scg#endif 11273131Scg return bus_setup_intr(dev, res, flags, hand, param, cookiep); 11373131Scg} 11473131Scg 11578214Scg/* return a locked channel */ 11677269Scgstruct pcm_channel * 11778214Scgpcm_chnalloc(struct snddev_info *d, int direction, pid_t pid) 11877269Scg{ 11977269Scg struct pcm_channel *c; 12077269Scg struct snddev_channel *sce; 12177269Scg 12278214Scg snd_mtxassert(d->lock); 12377269Scg SLIST_FOREACH(sce, &d->channels, link) { 12477269Scg c = sce->channel; 12577882Scg CHN_LOCK(c); 12677269Scg if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) { 12777269Scg c->flags |= CHN_F_BUSY; 12878214Scg c->pid = pid; 12977269Scg return c; 13077269Scg } 13177882Scg CHN_UNLOCK(c); 13277269Scg } 13377269Scg return NULL; 13477269Scg} 13577269Scg 13678214Scg/* release a locked channel and unlock it */ 13777269Scgint 13878214Scgpcm_chnrelease(struct pcm_channel *c) 13977269Scg{ 14078214Scg CHN_LOCKASSERT(c); 14177269Scg c->flags &= ~CHN_F_BUSY; 14278214Scg c->pid = -1; 14377882Scg CHN_UNLOCK(c); 14477269Scg return 0; 14577269Scg} 14677269Scg 14777269Scgint 14877269Scgpcm_chnref(struct pcm_channel *c, int ref) 14977269Scg{ 15077882Scg int r; 15177882Scg 15278214Scg CHN_LOCKASSERT(c); 15377269Scg c->refcount += ref; 15477882Scg r = c->refcount; 15577882Scg return r; 15677269Scg} 15777269Scg 15873127Scg#ifdef USING_DEVFS 15965340Scgstatic int 16065340Scgsysctl_hw_sndunit(SYSCTL_HANDLER_ARGS) 16165340Scg{ 16278214Scg struct snddev_info *d; 16365340Scg int error, unit; 16465340Scg 16565340Scg unit = snd_unit; 16665340Scg error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); 16765340Scg if (error == 0 && req->newptr != NULL) { 16878396Scg if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass)) 16978214Scg return EINVAL; 17078214Scg d = devclass_get_softc(pcm_devclass, unit); 17178395Scg if (d == NULL || SLIST_EMPTY(&d->channels)) 17278214Scg return EINVAL; 17365340Scg snd_unit = unit; 17465340Scg } 17565340Scg return (error); 17665340Scg} 17770617SjhbSYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW, 17865340Scg 0, sizeof(int), sysctl_hw_sndunit, "I", ""); 17973127Scg#endif 18065340Scg 18177269Scgstruct pcm_channel * 18277269Scgpcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo) 18350724Scg{ 18477269Scg struct pcm_channel *ch; 18565340Scg char *dirs; 18677269Scg int err; 18750724Scg 18877269Scg switch(dir) { 18977269Scg case PCMDIR_PLAY: 19077269Scg dirs = "play"; 19177269Scg break; 19277269Scg case PCMDIR_REC: 19377269Scg dirs = "record"; 19477269Scg break; 19577269Scg case PCMDIR_VIRTUAL: 19677269Scg dirs = "virtual"; 19777269Scg dir = PCMDIR_PLAY; 19877269Scg break; 19977269Scg default: 20077269Scg return NULL; 20177269Scg } 20265340Scg 20377269Scg ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 20477269Scg if (!ch) 20577269Scg return NULL; 20677269Scg 20777269Scg ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK); 20877269Scg if (!ch->methods) { 20977269Scg free(ch, M_DEVBUF); 21077269Scg return NULL; 21161344Scg } 21277269Scg 21377269Scg ch->pid = -1; 21477269Scg ch->parentsnddev = d; 21577269Scg ch->parentchannel = parent; 21677269Scg snprintf(ch->name, 32, "%s:%d:%s", device_get_nameunit(d->dev), d->chancount, dirs); 21777269Scg 21870134Scg err = chn_init(ch, devinfo, dir); 21970134Scg if (err) { 22077269Scg device_printf(d->dev, "chn_init() for channel %d (%s) failed: err = %d\n", d->chancount, dirs, err); 22177269Scg kobj_delete(ch->methods, M_DEVBUF); 22277269Scg free(ch, M_DEVBUF); 22377269Scg return NULL; 22455483Scg } 22577269Scg 22677269Scg return ch; 22777269Scg} 22877269Scg 22977269Scgint 23077269Scgpcm_chn_destroy(struct pcm_channel *ch) 23177269Scg{ 23277269Scg int err; 23377269Scg 23477269Scg err = chn_kill(ch); 23577269Scg if (err) { 23677269Scg device_printf(ch->parentsnddev->dev, "chn_kill() for %s failed, err = %d\n", ch->name, err); 23777269Scg return err; 23877269Scg } 23977269Scg 24077269Scg kobj_delete(ch->methods, M_DEVBUF); 24177269Scg free(ch, M_DEVBUF); 24277269Scg 24377269Scg return 0; 24477269Scg} 24577269Scg 24677269Scgint 24777269Scgpcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) 24877269Scg{ 24977269Scg struct snddev_channel *sce; 25077269Scg int unit = device_get_unit(d->dev); 25177269Scg 25278214Scg snd_mtxlock(d->lock); 25378214Scg 25477269Scg sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO); 25577269Scg if (!sce) { 25678214Scg snd_mtxunlock(d->lock); 25777269Scg return ENOMEM; 25877269Scg } 25977269Scg 26077269Scg sce->channel = ch; 26177269Scg SLIST_INSERT_HEAD(&d->channels, sce, link); 26277269Scg 26378362Scg dsp_register(unit, d->chancount); 26478362Scg d->chancount++; 26577269Scg 26677882Scg snd_mtxunlock(d->lock); 26777269Scg 26850724Scg return 0; 26950724Scg} 27050724Scg 27177269Scgint 27277269Scgpcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch) 27365340Scg{ 27477269Scg struct snddev_channel *sce; 27577269Scg int unit = device_get_unit(d->dev); 27665340Scg 27777882Scg snd_mtxlock(d->lock); 27877269Scg SLIST_FOREACH(sce, &d->channels, link) { 27977269Scg if (sce->channel == ch) 28077269Scg goto gotit; 28165340Scg } 28277882Scg snd_mtxunlock(d->lock); 28377269Scg return EINVAL; 28477269Scggotit: 28565340Scg d->chancount--; 28677269Scg SLIST_REMOVE(&d->channels, sce, snddev_channel, link); 28777269Scg free(sce, M_DEVBUF); 28877269Scg 28978362Scg dsp_unregister(unit, d->chancount); 29077882Scg snd_mtxunlock(d->lock); 29177269Scg 29265340Scg return 0; 29365340Scg} 29465340Scg 29550724Scgint 29677269Scgpcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 29777269Scg{ 29877269Scg struct snddev_info *d = device_get_softc(dev); 29977269Scg struct pcm_channel *ch; 30077269Scg int err; 30177269Scg 30277269Scg ch = pcm_chn_create(d, NULL, cls, dir, devinfo); 30377269Scg if (!ch) { 30477269Scg device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo); 30577269Scg return ENODEV; 30677269Scg } 30777269Scg err = pcm_chn_add(d, ch); 30877269Scg if (err) { 30977269Scg device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err); 31077269Scg pcm_chn_destroy(ch); 31177269Scg } 31277269Scg 31377269Scg return err; 31477269Scg} 31577269Scg 31677269Scgstatic int 31777269Scgpcm_killchan(device_t dev) 31877269Scg{ 31977269Scg struct snddev_info *d = device_get_softc(dev); 32077269Scg struct snddev_channel *sce; 32177269Scg 32277882Scg snd_mtxlock(d->lock); 32377269Scg sce = SLIST_FIRST(&d->channels); 32477882Scg snd_mtxunlock(d->lock); 32577269Scg 32677269Scg return pcm_chn_remove(d, sce->channel); 32777269Scg} 32877269Scg 32977269Scgint 33050724Scgpcm_setstatus(device_t dev, char *str) 33150724Scg{ 33274763Scg struct snddev_info *d = device_get_softc(dev); 33377882Scg 33477882Scg snd_mtxlock(d->lock); 33550724Scg strncpy(d->status, str, SND_STATUSLEN); 33677882Scg snd_mtxunlock(d->lock); 33750724Scg return 0; 33850724Scg} 33950724Scg 34050724Scgu_int32_t 34150724Scgpcm_getflags(device_t dev) 34250724Scg{ 34374763Scg struct snddev_info *d = device_get_softc(dev); 34477882Scg 34550724Scg return d->flags; 34650724Scg} 34750724Scg 34850724Scgvoid 34950724Scgpcm_setflags(device_t dev, u_int32_t val) 35050724Scg{ 35174763Scg struct snddev_info *d = device_get_softc(dev); 35278362Scg 35350724Scg d->flags = val; 35450724Scg} 35550724Scg 35658384Scgvoid * 35758384Scgpcm_getdevinfo(device_t dev) 35858384Scg{ 35974763Scg struct snddev_info *d = device_get_softc(dev); 36077882Scg 36158384Scg return d->devinfo; 36258384Scg} 36358384Scg 36450724Scg/* This is the generic init routine */ 36550724Scgint 36650724Scgpcm_register(device_t dev, void *devinfo, int numplay, int numrec) 36750724Scg{ 36874763Scg struct snddev_info *d = device_get_softc(dev); 36950724Scg 37077882Scg d->lock = snd_mtxcreate(device_get_nameunit(dev)); 37177882Scg snd_mtxlock(d->lock); 37277269Scg 37361886Scg d->dev = dev; 37450724Scg d->devinfo = devinfo; 37577269Scg d->chancount = 0; 37678362Scg d->inprog = 0; 37750724Scg 37878362Scg if (((numplay == 0) || (numrec == 0)) && (numplay != numrec)) 37978214Scg d->flags |= SD_F_SIMPLEX; 38078362Scg 38178214Scg d->fakechan = fkchan_setup(dev); 38278214Scg chn_init(d->fakechan, NULL, 0); 38355494Scg 38473127Scg#ifdef SND_DYNSYSCTL 38570680Sjhb sysctl_ctx_init(&d->sysctl_tree); 38670680Sjhb d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree, 38770943Sjhb SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO, 38870680Sjhb device_get_nameunit(dev), CTLFLAG_RD, 0, ""); 38970943Sjhb if (d->sysctl_tree_top == NULL) { 39070680Sjhb sysctl_ctx_free(&d->sysctl_tree); 39170680Sjhb goto no; 39270680Sjhb } 39373127Scg#endif 39478214Scg if (numplay > 0) 39578214Scg vchan_initsys(d); 39677882Scg snd_mtxunlock(d->lock); 39750724Scg return 0; 39850724Scgno: 39977882Scg snd_mtxfree(d->lock); 40050724Scg return ENXIO; 40150724Scg} 40250724Scg 40365340Scgint 40465340Scgpcm_unregister(device_t dev) 40565207Scg{ 40674763Scg struct snddev_info *d = device_get_softc(dev); 40777269Scg struct snddev_channel *sce; 40865207Scg 40977882Scg snd_mtxlock(d->lock); 41078362Scg if (d->inprog) { 41178362Scg device_printf(dev, "unregister: operation in progress"); 41278362Scg snd_mtxunlock(d->lock); 41378362Scg return EBUSY; 41478362Scg } 41577269Scg SLIST_FOREACH(sce, &d->channels, link) { 41677269Scg if (sce->channel->refcount > 0) { 41777269Scg device_printf(dev, "unregister: channel busy"); 41877882Scg snd_mtxunlock(d->lock); 41977269Scg return EBUSY; 42077269Scg } 42165487Scg } 42278362Scg if (mixer_uninit(dev)) { 42365487Scg device_printf(dev, "unregister: mixer busy"); 42477882Scg snd_mtxunlock(d->lock); 42565487Scg return EBUSY; 42665487Scg } 42765207Scg 42874763Scg#ifdef SND_DYNSYSCTL 42974763Scg d->sysctl_tree_top = NULL; 43074763Scg sysctl_ctx_free(&d->sysctl_tree); 43174763Scg#endif 43278395Scg while (!SLIST_EMPTY(&d->channels)) 43377269Scg pcm_killchan(dev); 43465207Scg 43574763Scg chn_kill(d->fakechan); 43674763Scg fkchan_kill(d->fakechan); 43773127Scg 43877882Scg snd_mtxfree(d->lock); 43965340Scg return 0; 44065207Scg} 44165207Scg 44265340Scgstatic moduledata_t sndpcm_mod = { 44365340Scg "snd_pcm", 44478362Scg NULL, 44565340Scg NULL 44665340Scg}; 44765340ScgDECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 44865340ScgMODULE_VERSION(snd_pcm, PCM_MODVER); 449