1139749Simp/*- 2193640Sariff * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org> 3193640Sariff * Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006 4193640Sariff * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org> 550724Scg * All rights reserved. 650724Scg * 750724Scg * Redistribution and use in source and binary forms, with or without 850724Scg * modification, are permitted provided that the following conditions 950724Scg * are met: 1050724Scg * 1. Redistributions of source code must retain the above copyright 1150724Scg * notice, this list of conditions and the following disclaimer. 1250724Scg * 2. Redistributions in binary form must reproduce the above copyright 1350724Scg * notice, this list of conditions and the following disclaimer in the 1450724Scg * documentation and/or other materials provided with the distribution. 1550724Scg * 1650724Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1750724Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1850724Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1950724Scg * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2050724Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2150724Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2250724Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2350724Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2450724Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2550724Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2650724Scg * SUCH DAMAGE. 2750724Scg */ 2850724Scg 29193640Sariff#ifdef HAVE_KERNEL_OPTION_HEADERS 30193640Sariff#include "opt_snd.h" 31193640Sariff#endif 32193640Sariff 3353465Scg#include <dev/sound/pcm/sound.h> 34170161Sariff#include <sys/ctype.h> 35248084Sattilio#include <sys/lock.h> 36248084Sattilio#include <sys/rwlock.h> 37193640Sariff#include <sys/sysent.h> 3850724Scg 39221803Savg#include <vm/vm.h> 40221803Savg#include <vm/vm_object.h> 41221803Savg#include <vm/vm_page.h> 42221803Savg#include <vm/vm_pager.h> 43221803Savg 4482180ScgSND_DECLARE_FILE("$FreeBSD$"); 4582180Scg 46170815Sariffstatic int dsp_mmap_allow_prot_exec = 0; 47170815SariffSYSCTL_INT(_hw_snd, OID_AUTO, compat_linux_mmap, CTLFLAG_RW, 48193640Sariff &dsp_mmap_allow_prot_exec, 0, 49193640Sariff "linux mmap compatibility (-1=force disable 0=auto 1=force enable)"); 50170815Sariff 51170161Sariffstruct dsp_cdevinfo { 52170161Sariff struct pcm_channel *rdch, *wrch; 53193640Sariff struct pcm_channel *volch; 54170815Sariff int busy, simplex; 55170815Sariff TAILQ_ENTRY(dsp_cdevinfo) link; 56170161Sariff}; 57170161Sariff 58170815Sariff#define PCM_RDCH(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->rdch) 59170815Sariff#define PCM_WRCH(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->wrch) 60193640Sariff#define PCM_VOLCH(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->volch) 61170815Sariff#define PCM_SIMPLEX(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->simplex) 62170161Sariff 63170815Sariff#define DSP_CDEVINFO_CACHESIZE 8 64170161Sariff 65170815Sariff#define DSP_REGISTERED(x, y) (PCM_REGISTERED(x) && \ 66170815Sariff (y) != NULL && (y)->si_drv1 != NULL) 67170161Sariff 6860976Scg#define OLDPCM_IOCTL 6960976Scg 7078362Scgstatic d_open_t dsp_open; 7178362Scgstatic d_close_t dsp_close; 7278362Scgstatic d_read_t dsp_read; 7378362Scgstatic d_write_t dsp_write; 7478362Scgstatic d_ioctl_t dsp_ioctl; 7578362Scgstatic d_poll_t dsp_poll; 7678362Scgstatic d_mmap_t dsp_mmap; 77221803Savgstatic d_mmap_single_t dsp_mmap_single; 7878362Scg 79124740Smatkstruct cdevsw dsp_cdevsw = { 80126080Sphk .d_version = D_VERSION, 81111815Sphk .d_open = dsp_open, 82111815Sphk .d_close = dsp_close, 83111815Sphk .d_read = dsp_read, 84111815Sphk .d_write = dsp_write, 85111815Sphk .d_ioctl = dsp_ioctl, 86111815Sphk .d_poll = dsp_poll, 87111815Sphk .d_mmap = dsp_mmap, 88221803Savg .d_mmap_single = dsp_mmap_single, 89111815Sphk .d_name = "dsp", 9078362Scg}; 9178362Scg 92170161Sariffstatic eventhandler_tag dsp_ehtag = NULL; 93170161Sariffstatic int dsp_umax = -1; 94170161Sariffstatic int dsp_cmax = -1; 9578362Scg 96162588Snetchildstatic int dsp_oss_syncgroup(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_syncgroup *group); 97162588Snetchildstatic int dsp_oss_syncstart(int sg_id); 98162588Snetchildstatic int dsp_oss_policy(struct pcm_channel *wrch, struct pcm_channel *rdch, int policy); 99162588Snetchildstatic int dsp_oss_cookedmode(struct pcm_channel *wrch, struct pcm_channel *rdch, int enabled); 100162588Snetchildstatic int dsp_oss_getchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map); 101162588Snetchildstatic int dsp_oss_setchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map); 102193640Sariffstatic int dsp_oss_getchannelmask(struct pcm_channel *wrch, struct pcm_channel *rdch, int *mask); 103193640Sariff#ifdef OSSV4_EXPERIMENT 104162588Snetchildstatic int dsp_oss_getlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label); 105162588Snetchildstatic int dsp_oss_setlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label); 106162588Snetchildstatic int dsp_oss_getsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song); 107162588Snetchildstatic int dsp_oss_setsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song); 108162588Snetchildstatic int dsp_oss_setname(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *name); 109162588Snetchild#endif 110162588Snetchild 11178362Scgstatic struct snddev_info * 112130585Sphkdsp_get_info(struct cdev *dev) 11378362Scg{ 114170161Sariff return (devclass_get_softc(pcm_devclass, PCMUNIT(dev))); 11578362Scg} 11678362Scg 117170815Sariffstatic uint32_t 118130585Sphkdsp_get_flags(struct cdev *dev) 11982180Scg{ 12082180Scg device_t bdev; 12182180Scg 122170161Sariff bdev = devclass_get_device(pcm_devclass, PCMUNIT(dev)); 12382180Scg 124170161Sariff return ((bdev != NULL) ? pcm_getflags(bdev) : 0xffffffff); 12582180Scg} 12682180Scg 12782180Scgstatic void 128170815Sariffdsp_set_flags(struct cdev *dev, uint32_t flags) 12982180Scg{ 13082180Scg device_t bdev; 13182180Scg 132170161Sariff bdev = devclass_get_device(pcm_devclass, PCMUNIT(dev)); 13382180Scg 134170161Sariff if (bdev != NULL) 135170161Sariff pcm_setflags(bdev, flags); 13682180Scg} 13782180Scg 13878362Scg/* 139154826Sjoel * return the channels associated with an open device instance. 14078362Scg * lock channels specified. 14178362Scg */ 14250724Scgstatic int 143170815Sariffgetchns(struct cdev *dev, struct pcm_channel **rdch, struct pcm_channel **wrch, 144170815Sariff uint32_t prio) 14550724Scg{ 14678362Scg struct snddev_info *d; 147170815Sariff struct pcm_channel *ch; 148170815Sariff uint32_t flags; 14978362Scg 150170815Sariff if (PCM_SIMPLEX(dev) != 0) { 151170815Sariff d = dsp_get_info(dev); 152170815Sariff if (!PCM_REGISTERED(d)) 153170815Sariff return (ENXIO); 154193640Sariff PCM_LOCK(d); 155170815Sariff PCM_WAIT(d); 156170815Sariff PCM_ACQUIRE(d); 157170815Sariff /* 158170815Sariff * Note: order is important - 159170815Sariff * pcm flags -> prio query flags -> wild guess 160170815Sariff */ 161170815Sariff ch = NULL; 162170815Sariff flags = dsp_get_flags(dev); 163170815Sariff if (flags & SD_F_PRIO_WR) { 164170815Sariff ch = PCM_RDCH(dev); 165170815Sariff PCM_RDCH(dev) = NULL; 166170815Sariff } else if (flags & SD_F_PRIO_RD) { 167170815Sariff ch = PCM_WRCH(dev); 168170815Sariff PCM_WRCH(dev) = NULL; 169170815Sariff } else if (prio & SD_F_PRIO_WR) { 170170815Sariff ch = PCM_RDCH(dev); 171170815Sariff PCM_RDCH(dev) = NULL; 172170815Sariff flags |= SD_F_PRIO_WR; 173170815Sariff } else if (prio & SD_F_PRIO_RD) { 174170815Sariff ch = PCM_WRCH(dev); 175170815Sariff PCM_WRCH(dev) = NULL; 176170815Sariff flags |= SD_F_PRIO_RD; 177170815Sariff } else if (PCM_WRCH(dev) != NULL) { 178170815Sariff ch = PCM_RDCH(dev); 179170815Sariff PCM_RDCH(dev) = NULL; 180170815Sariff flags |= SD_F_PRIO_WR; 181170815Sariff } else if (PCM_RDCH(dev) != NULL) { 182170815Sariff ch = PCM_WRCH(dev); 183170815Sariff PCM_WRCH(dev) = NULL; 184170815Sariff flags |= SD_F_PRIO_RD; 185170815Sariff } 186170815Sariff PCM_SIMPLEX(dev) = 0; 18782180Scg dsp_set_flags(dev, flags); 188170815Sariff if (ch != NULL) { 189170815Sariff CHN_LOCK(ch); 190170815Sariff pcm_chnref(ch, -1); 191170815Sariff pcm_chnrelease(ch); 192170815Sariff } 193170815Sariff PCM_RELEASE(d); 194193640Sariff PCM_UNLOCK(d); 19582180Scg } 19678362Scg 197170161Sariff *rdch = PCM_RDCH(dev); 198170161Sariff *wrch = PCM_WRCH(dev); 19978362Scg 200170815Sariff if (*rdch != NULL && (prio & SD_F_PRIO_RD)) 20178362Scg CHN_LOCK(*rdch); 202170815Sariff if (*wrch != NULL && (prio & SD_F_PRIO_WR)) 20378362Scg CHN_LOCK(*wrch); 20478362Scg 205170815Sariff return (0); 20650724Scg} 20750724Scg 20878362Scg/* unlock specified channels */ 20950724Scgstatic void 210170815Sariffrelchns(struct cdev *dev, struct pcm_channel *rdch, struct pcm_channel *wrch, 211170815Sariff uint32_t prio) 21250724Scg{ 213170815Sariff if (wrch != NULL && (prio & SD_F_PRIO_WR)) 21478362Scg CHN_UNLOCK(wrch); 215170815Sariff if (rdch != NULL && (prio & SD_F_PRIO_RD)) 21678362Scg CHN_UNLOCK(rdch); 21750724Scg} 21850724Scg 219170161Sariffstatic void 220170161Sariffdsp_cdevinfo_alloc(struct cdev *dev, 221193640Sariff struct pcm_channel *rdch, struct pcm_channel *wrch, 222193640Sariff struct pcm_channel *volch) 223170161Sariff{ 224170815Sariff struct snddev_info *d; 225170815Sariff struct dsp_cdevinfo *cdi; 226170815Sariff int simplex; 227170815Sariff 228170815Sariff d = dsp_get_info(dev); 229170815Sariff 230170815Sariff KASSERT(PCM_REGISTERED(d) && dev != NULL && dev->si_drv1 == NULL && 231193640Sariff ((rdch == NULL && wrch == NULL) || rdch != wrch), 232170161Sariff ("bogus %s(), what are you trying to accomplish here?", __func__)); 233170815Sariff PCM_BUSYASSERT(d); 234193640Sariff PCM_LOCKASSERT(d); 235170815Sariff 236170815Sariff simplex = (dsp_get_flags(dev) & SD_F_SIMPLEX) ? 1 : 0; 237170815Sariff 238170815Sariff /* 239170815Sariff * Scan for free instance entry and put it into the end of list. 240170815Sariff * Create new one if necessary. 241170815Sariff */ 242170815Sariff TAILQ_FOREACH(cdi, &d->dsp_cdevinfo_pool, link) { 243170815Sariff if (cdi->busy != 0) 244170815Sariff break; 245170815Sariff cdi->rdch = rdch; 246170815Sariff cdi->wrch = wrch; 247193640Sariff cdi->volch = volch; 248170815Sariff cdi->simplex = simplex; 249170815Sariff cdi->busy = 1; 250170815Sariff TAILQ_REMOVE(&d->dsp_cdevinfo_pool, cdi, link); 251170815Sariff TAILQ_INSERT_TAIL(&d->dsp_cdevinfo_pool, cdi, link); 252170815Sariff dev->si_drv1 = cdi; 253170815Sariff return; 254170815Sariff } 255193640Sariff PCM_UNLOCK(d); 256170815Sariff cdi = malloc(sizeof(*cdi), M_DEVBUF, M_WAITOK | M_ZERO); 257193640Sariff PCM_LOCK(d); 258170815Sariff cdi->rdch = rdch; 259170815Sariff cdi->wrch = wrch; 260193640Sariff cdi->volch = volch; 261170815Sariff cdi->simplex = simplex; 262170815Sariff cdi->busy = 1; 263170815Sariff TAILQ_INSERT_TAIL(&d->dsp_cdevinfo_pool, cdi, link); 264170815Sariff dev->si_drv1 = cdi; 265170161Sariff} 266170161Sariff 267170161Sariffstatic void 268170161Sariffdsp_cdevinfo_free(struct cdev *dev) 269170161Sariff{ 270170815Sariff struct snddev_info *d; 271170815Sariff struct dsp_cdevinfo *cdi, *tmp; 272170815Sariff uint32_t flags; 273170815Sariff int i; 274170815Sariff 275170815Sariff d = dsp_get_info(dev); 276170815Sariff 277170815Sariff KASSERT(PCM_REGISTERED(d) && dev != NULL && dev->si_drv1 != NULL && 278193640Sariff PCM_RDCH(dev) == NULL && PCM_WRCH(dev) == NULL && 279193640Sariff PCM_VOLCH(dev) == NULL, 280170161Sariff ("bogus %s(), what are you trying to accomplish here?", __func__)); 281170815Sariff PCM_BUSYASSERT(d); 282193640Sariff PCM_LOCKASSERT(d); 283170161Sariff 284170815Sariff cdi = dev->si_drv1; 285170161Sariff dev->si_drv1 = NULL; 286170815Sariff cdi->rdch = NULL; 287170815Sariff cdi->wrch = NULL; 288193640Sariff cdi->volch = NULL; 289170815Sariff cdi->simplex = 0; 290170815Sariff cdi->busy = 0; 291170815Sariff 292170815Sariff /* 293170815Sariff * Once it is free, move it back to the beginning of list for 294170815Sariff * faster new entry allocation. 295170815Sariff */ 296170815Sariff TAILQ_REMOVE(&d->dsp_cdevinfo_pool, cdi, link); 297170815Sariff TAILQ_INSERT_HEAD(&d->dsp_cdevinfo_pool, cdi, link); 298170815Sariff 299170815Sariff /* 300170815Sariff * Scan the list, cache free entries up to DSP_CDEVINFO_CACHESIZE. 301170815Sariff * Reset simplex flags. 302170815Sariff */ 303170815Sariff flags = dsp_get_flags(dev) & ~SD_F_PRIO_SET; 304170815Sariff i = DSP_CDEVINFO_CACHESIZE; 305170815Sariff TAILQ_FOREACH_SAFE(cdi, &d->dsp_cdevinfo_pool, link, tmp) { 306170815Sariff if (cdi->busy != 0) { 307170815Sariff if (cdi->simplex == 0) { 308170815Sariff if (cdi->rdch != NULL) 309170815Sariff flags |= SD_F_PRIO_RD; 310170815Sariff if (cdi->wrch != NULL) 311170815Sariff flags |= SD_F_PRIO_WR; 312170815Sariff } 313170815Sariff } else { 314170815Sariff if (i == 0) { 315170815Sariff TAILQ_REMOVE(&d->dsp_cdevinfo_pool, cdi, link); 316170815Sariff free(cdi, M_DEVBUF); 317170815Sariff } else 318170815Sariff i--; 319170815Sariff } 320170815Sariff } 321170815Sariff dsp_set_flags(dev, flags); 322170161Sariff} 323170161Sariff 324170815Sariffvoid 325170815Sariffdsp_cdevinfo_init(struct snddev_info *d) 326170815Sariff{ 327170815Sariff struct dsp_cdevinfo *cdi; 328170815Sariff int i; 329170815Sariff 330170815Sariff KASSERT(d != NULL, ("NULL snddev_info")); 331170815Sariff PCM_BUSYASSERT(d); 332193640Sariff PCM_UNLOCKASSERT(d); 333170815Sariff 334170815Sariff TAILQ_INIT(&d->dsp_cdevinfo_pool); 335170815Sariff for (i = 0; i < DSP_CDEVINFO_CACHESIZE; i++) { 336170815Sariff cdi = malloc(sizeof(*cdi), M_DEVBUF, M_WAITOK | M_ZERO); 337170815Sariff TAILQ_INSERT_HEAD(&d->dsp_cdevinfo_pool, cdi, link); 338170815Sariff } 339170815Sariff} 340170815Sariff 341170815Sariffvoid 342170815Sariffdsp_cdevinfo_flush(struct snddev_info *d) 343170815Sariff{ 344170815Sariff struct dsp_cdevinfo *cdi, *tmp; 345170815Sariff 346170815Sariff KASSERT(d != NULL, ("NULL snddev_info")); 347170815Sariff PCM_BUSYASSERT(d); 348193640Sariff PCM_UNLOCKASSERT(d); 349170815Sariff 350170815Sariff cdi = TAILQ_FIRST(&d->dsp_cdevinfo_pool); 351170815Sariff while (cdi != NULL) { 352170815Sariff tmp = TAILQ_NEXT(cdi, link); 353170815Sariff free(cdi, M_DEVBUF); 354170815Sariff cdi = tmp; 355170815Sariff } 356170815Sariff TAILQ_INIT(&d->dsp_cdevinfo_pool); 357170815Sariff} 358170815Sariff 359170161Sariff/* duplex / simplex cdev type */ 360170161Sariffenum { 361170161Sariff DSP_CDEV_TYPE_RDONLY, /* simplex read-only (record) */ 362170161Sariff DSP_CDEV_TYPE_WRONLY, /* simplex write-only (play) */ 363193640Sariff DSP_CDEV_TYPE_RDWR /* duplex read, write, or both */ 364170161Sariff}; 365170161Sariff 366193640Sariffenum { 367193640Sariff DSP_CDEV_VOLCTL_NONE, 368193640Sariff DSP_CDEV_VOLCTL_READ, 369193640Sariff DSP_CDEV_VOLCTL_WRITE 370193640Sariff}; 371193640Sariff 372170161Sariff#define DSP_F_VALID(x) ((x) & (FREAD | FWRITE)) 373170161Sariff#define DSP_F_DUPLEX(x) (((x) & (FREAD | FWRITE)) == (FREAD | FWRITE)) 374170161Sariff#define DSP_F_SIMPLEX(x) (!DSP_F_DUPLEX(x)) 375170161Sariff#define DSP_F_READ(x) ((x) & FREAD) 376170161Sariff#define DSP_F_WRITE(x) ((x) & FWRITE) 377170161Sariff 378170161Sariffstatic const struct { 379170161Sariff int type; 380170161Sariff char *name; 381170161Sariff char *sep; 382193640Sariff char *alias; 383170161Sariff int use_sep; 384170161Sariff int hw; 385170161Sariff int max; 386193640Sariff int volctl; 387170161Sariff uint32_t fmt, spd; 388170161Sariff int query; 389170161Sariff} dsp_cdevs[] = { 390193640Sariff { SND_DEV_DSP, "dsp", ".", NULL, 0, 0, 0, 0, 391193640Sariff SND_FORMAT(AFMT_U8, 1, 0), DSP_DEFAULT_SPEED, 392193640Sariff DSP_CDEV_TYPE_RDWR }, 393193640Sariff { SND_DEV_AUDIO, "audio", ".", NULL, 0, 0, 0, 0, 394193640Sariff SND_FORMAT(AFMT_MU_LAW, 1, 0), DSP_DEFAULT_SPEED, 395193640Sariff DSP_CDEV_TYPE_RDWR }, 396193640Sariff { SND_DEV_DSP16, "dspW", ".", NULL, 0, 0, 0, 0, 397193640Sariff SND_FORMAT(AFMT_S16_LE, 1, 0), DSP_DEFAULT_SPEED, 398193640Sariff DSP_CDEV_TYPE_RDWR }, 399193640Sariff { SND_DEV_DSPHW_PLAY, "dsp", ".p", NULL, 1, 1, SND_MAXHWCHAN, 1, 400193640Sariff SND_FORMAT(AFMT_S16_LE, 2, 0), 48000, DSP_CDEV_TYPE_WRONLY }, 401193640Sariff { SND_DEV_DSPHW_VPLAY, "dsp", ".vp", NULL, 1, 1, SND_MAXVCHANS, 1, 402193640Sariff SND_FORMAT(AFMT_S16_LE, 2, 0), 48000, DSP_CDEV_TYPE_WRONLY }, 403193640Sariff { SND_DEV_DSPHW_REC, "dsp", ".r", NULL, 1, 1, SND_MAXHWCHAN, 1, 404193640Sariff SND_FORMAT(AFMT_S16_LE, 2, 0), 48000, DSP_CDEV_TYPE_RDONLY }, 405193640Sariff { SND_DEV_DSPHW_VREC, "dsp", ".vr", NULL, 1, 1, SND_MAXVCHANS, 1, 406193640Sariff SND_FORMAT(AFMT_S16_LE, 2, 0), 48000, DSP_CDEV_TYPE_RDONLY }, 407193640Sariff { SND_DEV_DSPHW_CD, "dspcd", ".", NULL, 0, 0, 0, 0, 408193640Sariff SND_FORMAT(AFMT_S16_LE, 2, 0), 44100, DSP_CDEV_TYPE_RDWR }, 409193640Sariff /* Low priority, OSSv4 aliases. */ 410193640Sariff { SND_DEV_DSP, "dsp_ac3", ".", "dsp", 0, 0, 0, 0, 411193640Sariff SND_FORMAT(AFMT_U8, 1, 0), DSP_DEFAULT_SPEED, 412193640Sariff DSP_CDEV_TYPE_RDWR }, 413193640Sariff { SND_DEV_DSP, "dsp_mmap", ".", "dsp", 0, 0, 0, 0, 414193640Sariff SND_FORMAT(AFMT_U8, 1, 0), DSP_DEFAULT_SPEED, 415193640Sariff DSP_CDEV_TYPE_RDWR }, 416193640Sariff { SND_DEV_DSP, "dsp_multich", ".", "dsp", 0, 0, 0, 0, 417193640Sariff SND_FORMAT(AFMT_U8, 1, 0), DSP_DEFAULT_SPEED, 418193640Sariff DSP_CDEV_TYPE_RDWR }, 419193640Sariff { SND_DEV_DSP, "dsp_spdifout", ".", "dsp", 0, 0, 0, 0, 420193640Sariff SND_FORMAT(AFMT_U8, 1, 0), DSP_DEFAULT_SPEED, 421193640Sariff DSP_CDEV_TYPE_RDWR }, 422193640Sariff { SND_DEV_DSP, "dsp_spdifin", ".", "dsp", 0, 0, 0, 0, 423193640Sariff SND_FORMAT(AFMT_U8, 1, 0), DSP_DEFAULT_SPEED, 424193640Sariff DSP_CDEV_TYPE_RDWR }, 425170161Sariff}; 426170161Sariff 427170815Sariff#define DSP_FIXUP_ERROR() do { \ 428170815Sariff prio = dsp_get_flags(i_dev); \ 429170815Sariff if (!DSP_F_VALID(flags)) \ 430170815Sariff error = EINVAL; \ 431170815Sariff if (!DSP_F_DUPLEX(flags) && \ 432170815Sariff ((DSP_F_READ(flags) && d->reccount == 0) || \ 433170815Sariff (DSP_F_WRITE(flags) && d->playcount == 0))) \ 434170815Sariff error = ENOTSUP; \ 435170815Sariff else if (!DSP_F_DUPLEX(flags) && (prio & SD_F_SIMPLEX) && \ 436170815Sariff ((DSP_F_READ(flags) && (prio & SD_F_PRIO_WR)) || \ 437170815Sariff (DSP_F_WRITE(flags) && (prio & SD_F_PRIO_RD)))) \ 438170815Sariff error = EBUSY; \ 439170815Sariff else if (DSP_REGISTERED(d, i_dev)) \ 440170815Sariff error = EBUSY; \ 441193640Sariff} while (0) 442170815Sariff 44378362Scgstatic int 444130585Sphkdsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td) 44550724Scg{ 44674763Scg struct pcm_channel *rdch, *wrch; 44778362Scg struct snddev_info *d; 448193640Sariff uint32_t fmt, spd, prio, volctl; 449170815Sariff int i, error, rderror, wrerror, devtype, wdevunit, rdevunit; 45050724Scg 451170161Sariff /* Kind of impossible.. */ 452156929Sariff if (i_dev == NULL || td == NULL) 453170815Sariff return (ENODEV); 454156929Sariff 45578362Scg d = dsp_get_info(i_dev); 456170815Sariff if (!PCM_REGISTERED(d)) 457170815Sariff return (EBADF); 45878362Scg 459170815Sariff PCM_GIANT_ENTER(d); 460170815Sariff 461170161Sariff /* Lock snddev so nobody else can monkey with it. */ 462193640Sariff PCM_LOCK(d); 463170815Sariff PCM_WAIT(d); 46478214Scg 465170161Sariff /* 466170161Sariff * Try to acquire cloned device before someone else pick it. 467170161Sariff * ENODEV means this is not a cloned droids. 468170161Sariff */ 469170161Sariff error = snd_clone_acquire(i_dev); 470170161Sariff if (!(error == 0 || error == ENODEV)) { 471170815Sariff DSP_FIXUP_ERROR(); 472193640Sariff PCM_UNLOCK(d); 473170815Sariff PCM_GIANT_EXIT(d); 474170815Sariff return (error); 475170161Sariff } 47678214Scg 477170815Sariff error = 0; 478170815Sariff DSP_FIXUP_ERROR(); 47978214Scg 480170161Sariff if (error != 0) { 481170161Sariff (void)snd_clone_release(i_dev); 482193640Sariff PCM_UNLOCK(d); 483170815Sariff PCM_GIANT_EXIT(d); 484170815Sariff return (error); 485170161Sariff } 48678214Scg 487170161Sariff /* 488170815Sariff * That is just enough. Acquire and unlock pcm lock so 489170815Sariff * the other will just have to wait until we finish doing 490170815Sariff * everything. 491170161Sariff */ 492170815Sariff PCM_ACQUIRE(d); 493193640Sariff PCM_UNLOCK(d); 494170161Sariff 495170161Sariff devtype = PCMDEV(i_dev); 496170161Sariff wdevunit = -1; 497170161Sariff rdevunit = -1; 498170161Sariff fmt = 0; 499170161Sariff spd = 0; 500193640Sariff volctl = DSP_CDEV_VOLCTL_NONE; 501170161Sariff 502170161Sariff for (i = 0; i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])); i++) { 503193640Sariff if (devtype != dsp_cdevs[i].type || dsp_cdevs[i].alias != NULL) 504170161Sariff continue; 505193640Sariff /* 506193640Sariff * Volume control only valid for DSPHW devices, 507193640Sariff * and it must be opened in opposite direction be it 508193640Sariff * simplex or duplex. Anything else will be handled 509193640Sariff * as usual. 510193640Sariff */ 511193640Sariff if (dsp_cdevs[i].query == DSP_CDEV_TYPE_WRONLY) { 512193640Sariff if (dsp_cdevs[i].volctl != 0 && 513193640Sariff DSP_F_READ(flags)) { 514193640Sariff volctl = DSP_CDEV_VOLCTL_WRITE; 515193640Sariff flags &= ~FREAD; 516193640Sariff flags |= FWRITE; 517193640Sariff } 518193640Sariff if (DSP_F_READ(flags)) { 519193640Sariff (void)snd_clone_release(i_dev); 520193640Sariff PCM_RELEASE_QUICK(d); 521193640Sariff PCM_GIANT_EXIT(d); 522193640Sariff return (ENOTSUP); 523193640Sariff } 524170161Sariff wdevunit = dev2unit(i_dev); 525193640Sariff } else if (dsp_cdevs[i].query == DSP_CDEV_TYPE_RDONLY) { 526193640Sariff if (dsp_cdevs[i].volctl != 0 && 527193640Sariff DSP_F_WRITE(flags)) { 528193640Sariff volctl = DSP_CDEV_VOLCTL_READ; 529193640Sariff flags &= ~FWRITE; 530193640Sariff flags |= FREAD; 531193640Sariff } 532193640Sariff if (DSP_F_WRITE(flags)) { 533193640Sariff (void)snd_clone_release(i_dev); 534193640Sariff PCM_RELEASE_QUICK(d); 535193640Sariff PCM_GIANT_EXIT(d); 536193640Sariff return (ENOTSUP); 537193640Sariff } 538170161Sariff rdevunit = dev2unit(i_dev); 539193640Sariff } 540170161Sariff fmt = dsp_cdevs[i].fmt; 541170161Sariff spd = dsp_cdevs[i].spd; 54283089Scg break; 543170161Sariff } 54483089Scg 545170161Sariff /* No matching devtype? */ 546170161Sariff if (fmt == 0 || spd == 0) 54778214Scg panic("impossible devtype %d", devtype); 54878214Scg 549170161Sariff rdch = NULL; 550170161Sariff wrch = NULL; 551170815Sariff rderror = 0; 552170815Sariff wrerror = 0; 553102525Sorion 554118927Scg /* 555118927Scg * if we get here, the open request is valid- either: 556118927Scg * * we were previously not open 557118927Scg * * we were open for play xor record and the opener wants 558118927Scg * the non-open direction 559118927Scg */ 560170161Sariff if (DSP_F_READ(flags)) { 56178214Scg /* open for read */ 562170815Sariff rderror = pcm_chnalloc(d, &rdch, PCMDIR_REC, 563193640Sariff td->td_proc->p_pid, td->td_proc->p_comm, rdevunit); 564156929Sariff 565193640Sariff if (rderror == 0 && chn_reset(rdch, fmt, spd) != 0) 566170815Sariff rderror = ENXIO; 567156929Sariff 568193640Sariff if (volctl == DSP_CDEV_VOLCTL_READ) 569193640Sariff rderror = 0; 570193640Sariff 571170815Sariff if (rderror != 0) { 572170161Sariff if (rdch != NULL) 573156929Sariff pcm_chnrelease(rdch); 574170815Sariff if (!DSP_F_DUPLEX(flags)) { 575170815Sariff (void)snd_clone_release(i_dev); 576170815Sariff PCM_RELEASE_QUICK(d); 577170815Sariff PCM_GIANT_EXIT(d); 578170815Sariff return (rderror); 579170815Sariff } 580170815Sariff rdch = NULL; 581193640Sariff } else if (volctl == DSP_CDEV_VOLCTL_READ) { 582193640Sariff if (rdch != NULL) { 583193640Sariff pcm_chnref(rdch, 1); 584193640Sariff pcm_chnrelease(rdch); 585193640Sariff } 586170815Sariff } else { 587170815Sariff if (flags & O_NONBLOCK) 588170815Sariff rdch->flags |= CHN_F_NBIO; 589193640Sariff if (flags & O_EXCL) 590193640Sariff rdch->flags |= CHN_F_EXCLUSIVE; 591170815Sariff pcm_chnref(rdch, 1); 592193640Sariff if (volctl == DSP_CDEV_VOLCTL_NONE) 593193640Sariff chn_vpc_reset(rdch, SND_VOL_C_PCM, 0); 594170815Sariff CHN_UNLOCK(rdch); 59578214Scg } 59650724Scg } 597122461Sscottl 598170161Sariff if (DSP_F_WRITE(flags)) { 599170161Sariff /* open for write */ 600170815Sariff wrerror = pcm_chnalloc(d, &wrch, PCMDIR_PLAY, 601193640Sariff td->td_proc->p_pid, td->td_proc->p_comm, wdevunit); 602122461Sscottl 603193640Sariff if (wrerror == 0 && chn_reset(wrch, fmt, spd) != 0) 604170815Sariff wrerror = ENXIO; 605122461Sscottl 606193640Sariff if (volctl == DSP_CDEV_VOLCTL_WRITE) 607193640Sariff wrerror = 0; 608193640Sariff 609170815Sariff if (wrerror != 0) { 610170161Sariff if (wrch != NULL) 611170161Sariff pcm_chnrelease(wrch); 612170815Sariff if (!DSP_F_DUPLEX(flags)) { 613170815Sariff if (rdch != NULL) { 614170815Sariff /* 615170815Sariff * Lock, deref and release previously 616170815Sariff * created record channel 617170815Sariff */ 618170815Sariff CHN_LOCK(rdch); 619170815Sariff pcm_chnref(rdch, -1); 620170815Sariff pcm_chnrelease(rdch); 621170815Sariff } 622170815Sariff (void)snd_clone_release(i_dev); 623170815Sariff PCM_RELEASE_QUICK(d); 624170815Sariff PCM_GIANT_EXIT(d); 625170815Sariff return (wrerror); 626170161Sariff } 627170815Sariff wrch = NULL; 628193640Sariff } else if (volctl == DSP_CDEV_VOLCTL_WRITE) { 629193640Sariff if (wrch != NULL) { 630193640Sariff pcm_chnref(wrch, 1); 631193640Sariff pcm_chnrelease(wrch); 632193640Sariff } 633170815Sariff } else { 634170815Sariff if (flags & O_NONBLOCK) 635170815Sariff wrch->flags |= CHN_F_NBIO; 636193640Sariff if (flags & O_EXCL) 637193640Sariff wrch->flags |= CHN_F_EXCLUSIVE; 638170815Sariff pcm_chnref(wrch, 1); 639193640Sariff if (volctl == DSP_CDEV_VOLCTL_NONE) 640193640Sariff chn_vpc_reset(wrch, SND_VOL_C_PCM, 0); 641170815Sariff CHN_UNLOCK(wrch); 642122461Sscottl } 643170815Sariff } 644122461Sscottl 645122461Sscottl 646193640Sariff PCM_LOCK(d); 647170815Sariff 648170161Sariff /* 649170815Sariff * We're done. Allocate channels information for this cdev. 650170815Sariff */ 651193640Sariff switch (volctl) { 652193640Sariff case DSP_CDEV_VOLCTL_READ: 653193640Sariff KASSERT(wrch == NULL, ("wrch=%p not null!", wrch)); 654193640Sariff dsp_cdevinfo_alloc(i_dev, NULL, NULL, rdch); 655193640Sariff break; 656193640Sariff case DSP_CDEV_VOLCTL_WRITE: 657193640Sariff KASSERT(rdch == NULL, ("rdch=%p not null!", rdch)); 658193640Sariff dsp_cdevinfo_alloc(i_dev, NULL, NULL, wrch); 659193640Sariff break; 660193640Sariff case DSP_CDEV_VOLCTL_NONE: 661193640Sariff default: 662193640Sariff if (wrch == NULL && rdch == NULL) { 663193640Sariff (void)snd_clone_release(i_dev); 664193640Sariff PCM_RELEASE(d); 665193640Sariff PCM_UNLOCK(d); 666193640Sariff PCM_GIANT_EXIT(d); 667193640Sariff if (wrerror != 0) 668193640Sariff return (wrerror); 669193640Sariff if (rderror != 0) 670193640Sariff return (rderror); 671193640Sariff return (EINVAL); 672193640Sariff } 673193640Sariff dsp_cdevinfo_alloc(i_dev, rdch, wrch, NULL); 674193640Sariff if (rdch != NULL) 675193640Sariff CHN_INSERT_HEAD(d, rdch, channels.pcm.opened); 676193640Sariff if (wrch != NULL) 677193640Sariff CHN_INSERT_HEAD(d, wrch, channels.pcm.opened); 678193640Sariff break; 679193640Sariff } 680170815Sariff 681170815Sariff /* 682170161Sariff * Increase clone refcount for its automatic garbage collector. 683170161Sariff */ 684170161Sariff (void)snd_clone_ref(i_dev); 685122461Sscottl 686170815Sariff PCM_RELEASE(d); 687193640Sariff PCM_UNLOCK(d); 688170161Sariff 689170815Sariff PCM_GIANT_LEAVE(d); 690170161Sariff 691170815Sariff return (0); 69250724Scg} 69350724Scg 69478362Scgstatic int 695130585Sphkdsp_close(struct cdev *i_dev, int flags, int mode, struct thread *td) 69650724Scg{ 697193640Sariff struct pcm_channel *rdch, *wrch, *volch; 69878362Scg struct snddev_info *d; 699193640Sariff int sg_ids, rdref, wdref; 70050724Scg 70178362Scg d = dsp_get_info(i_dev); 702170815Sariff if (!DSP_REGISTERED(d, i_dev)) 703170815Sariff return (EBADF); 704170815Sariff 705170815Sariff PCM_GIANT_ENTER(d); 706170815Sariff 707193640Sariff PCM_LOCK(d); 708170815Sariff PCM_WAIT(d); 709193640Sariff PCM_ACQUIRE(d); 710170815Sariff 711170161Sariff rdch = PCM_RDCH(i_dev); 712170161Sariff wrch = PCM_WRCH(i_dev); 713193640Sariff volch = PCM_VOLCH(i_dev); 71450724Scg 715193640Sariff PCM_RDCH(i_dev) = NULL; 716193640Sariff PCM_WRCH(i_dev) = NULL; 717193640Sariff PCM_VOLCH(i_dev) = NULL; 718162588Snetchild 719193640Sariff rdref = -1; 720193640Sariff wdref = -1; 721193640Sariff 722193640Sariff if (volch != NULL) { 723193640Sariff if (volch == rdch) 724193640Sariff rdref--; 725193640Sariff else if (volch == wrch) 726193640Sariff wdref--; 727193640Sariff else { 728193640Sariff CHN_LOCK(volch); 729193640Sariff pcm_chnref(volch, -1); 730193640Sariff CHN_UNLOCK(volch); 731193640Sariff } 732193640Sariff } 733193640Sariff 734193640Sariff if (rdch != NULL) 735193640Sariff CHN_REMOVE(d, rdch, channels.pcm.opened); 736193640Sariff if (wrch != NULL) 737193640Sariff CHN_REMOVE(d, wrch, channels.pcm.opened); 738193640Sariff 739193640Sariff if (rdch != NULL || wrch != NULL) { 740193640Sariff PCM_UNLOCK(d); 741193640Sariff if (rdch != NULL) { 742162588Snetchild /* 743162588Snetchild * The channel itself need not be locked because: 744193640Sariff * a) Adding a channel to a syncgroup happens only 745193640Sariff * in dsp_ioctl(), which cannot run concurrently 746193640Sariff * to dsp_close(). 747193640Sariff * b) The syncmember pointer (sm) is protected by 748193640Sariff * the global syncgroup list lock. 749193640Sariff * c) A channel can't just disappear, invalidating 750193640Sariff * pointers, unless it's closed/dereferenced 751193640Sariff * first. 752162588Snetchild */ 753162588Snetchild PCM_SG_LOCK(); 754170815Sariff sg_ids = chn_syncdestroy(rdch); 755162588Snetchild PCM_SG_UNLOCK(); 756170815Sariff if (sg_ids != 0) 757170815Sariff free_unr(pcmsg_unrhdr, sg_ids); 758162588Snetchild 759122461Sscottl CHN_LOCK(rdch); 760193640Sariff pcm_chnref(rdch, rdref); 761122461Sscottl chn_abort(rdch); /* won't sleep */ 762193640Sariff rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MMAP | 763193640Sariff CHN_F_DEAD | CHN_F_EXCLUSIVE); 764193640Sariff chn_reset(rdch, 0, 0); 765122461Sscottl pcm_chnrelease(rdch); 766122461Sscottl } 767193640Sariff if (wrch != NULL) { 768162588Snetchild /* 769162588Snetchild * Please see block above. 770162588Snetchild */ 771162588Snetchild PCM_SG_LOCK(); 772170815Sariff sg_ids = chn_syncdestroy(wrch); 773162588Snetchild PCM_SG_UNLOCK(); 774170815Sariff if (sg_ids != 0) 775170815Sariff free_unr(pcmsg_unrhdr, sg_ids); 776162588Snetchild 777122461Sscottl CHN_LOCK(wrch); 778193640Sariff pcm_chnref(wrch, wdref); 779122461Sscottl chn_flush(wrch); /* may sleep */ 780193640Sariff wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MMAP | 781193640Sariff CHN_F_DEAD | CHN_F_EXCLUSIVE); 782193640Sariff chn_reset(wrch, 0, 0); 783122461Sscottl pcm_chnrelease(wrch); 784122461Sscottl } 785193640Sariff PCM_LOCK(d); 786193640Sariff } 787156929Sariff 788193640Sariff dsp_cdevinfo_free(i_dev); 789193640Sariff /* 790193640Sariff * Release clone busy state and unref it so the automatic 791193640Sariff * garbage collector will get the hint and do the remaining 792193640Sariff * cleanup process. 793193640Sariff */ 794193640Sariff (void)snd_clone_release(i_dev); 795179345Sariff 796193640Sariff /* 797193640Sariff * destroy_dev() might sleep, so release pcm lock 798193640Sariff * here and rely on pcm cv serialization. 799193640Sariff */ 800193640Sariff PCM_UNLOCK(d); 801193640Sariff (void)snd_clone_unref(i_dev); 802193640Sariff PCM_LOCK(d); 803162588Snetchild 804193640Sariff PCM_RELEASE(d); 805193640Sariff PCM_UNLOCK(d); 806162588Snetchild 807170815Sariff PCM_GIANT_LEAVE(d); 808162588Snetchild 809170815Sariff return (0); 81050724Scg} 81150724Scg 812170815Sariffstatic __inline int 813170815Sariffdsp_io_ops(struct cdev *i_dev, struct uio *buf) 81450724Scg{ 815170815Sariff struct snddev_info *d; 816170815Sariff struct pcm_channel **ch, *rdch, *wrch; 817170815Sariff int (*chn_io)(struct pcm_channel *, struct uio *); 818170815Sariff int prio, ret; 819170815Sariff pid_t runpid; 82050724Scg 821170815Sariff KASSERT(i_dev != NULL && buf != NULL && 822170815Sariff (buf->uio_rw == UIO_READ || buf->uio_rw == UIO_WRITE), 823170815Sariff ("%s(): io train wreck!", __func__)); 82474763Scg 825170815Sariff d = dsp_get_info(i_dev); 826170815Sariff if (!DSP_REGISTERED(d, i_dev)) 827170815Sariff return (EBADF); 82874763Scg 829170815Sariff PCM_GIANT_ENTER(d); 830170815Sariff 831170815Sariff switch (buf->uio_rw) { 832170815Sariff case UIO_READ: 833170815Sariff prio = SD_F_PRIO_RD; 834170815Sariff ch = &rdch; 835170815Sariff chn_io = chn_read; 836170815Sariff break; 837170815Sariff case UIO_WRITE: 838170815Sariff prio = SD_F_PRIO_WR; 839170815Sariff ch = &wrch; 840170815Sariff chn_io = chn_write; 841170815Sariff break; 842170815Sariff default: 843170815Sariff panic("invalid/corrupted uio direction: %d", buf->uio_rw); 844170815Sariff break; 84574763Scg } 84674763Scg 847170815Sariff rdch = NULL; 848170815Sariff wrch = NULL; 849170815Sariff runpid = buf->uio_td->td_proc->p_pid; 85050724Scg 851170815Sariff getchns(i_dev, &rdch, &wrch, prio); 85250724Scg 853170815Sariff if (*ch == NULL || !((*ch)->flags & CHN_F_BUSY)) { 854170815Sariff PCM_GIANT_EXIT(d); 855170815Sariff return (EBADF); 856170815Sariff } 85774763Scg 858193640Sariff if (((*ch)->flags & (CHN_F_MMAP | CHN_F_DEAD)) || 859170815Sariff (((*ch)->flags & CHN_F_RUNNING) && (*ch)->pid != runpid)) { 860170815Sariff relchns(i_dev, rdch, wrch, prio); 861170815Sariff PCM_GIANT_EXIT(d); 862170815Sariff return (EINVAL); 863170815Sariff } else if (!((*ch)->flags & CHN_F_RUNNING)) { 864170815Sariff (*ch)->flags |= CHN_F_RUNNING; 865170815Sariff (*ch)->pid = runpid; 86674763Scg } 867162588Snetchild 868162588Snetchild /* 869170815Sariff * chn_read/write must give up channel lock in order to copy bytes 870170815Sariff * from/to userland, so up the "in progress" counter to make sure 871170815Sariff * someone else doesn't come along and muss up the buffer. 872162588Snetchild */ 873170815Sariff ++(*ch)->inprog; 874170815Sariff ret = chn_io(*ch, buf); 875170815Sariff --(*ch)->inprog; 876162588Snetchild 877170815Sariff CHN_BROADCAST(&(*ch)->cv); 87874763Scg 879170815Sariff relchns(i_dev, rdch, wrch, prio); 880170815Sariff 881170815Sariff PCM_GIANT_LEAVE(d); 882170815Sariff 883170815Sariff return (ret); 88450724Scg} 88550724Scg 88678362Scgstatic int 887170815Sariffdsp_read(struct cdev *i_dev, struct uio *buf, int flag) 888170815Sariff{ 889170815Sariff return (dsp_io_ops(i_dev, buf)); 890170815Sariff} 891170815Sariff 892170815Sariffstatic int 893170815Sariffdsp_write(struct cdev *i_dev, struct uio *buf, int flag) 894170815Sariff{ 895170815Sariff return (dsp_io_ops(i_dev, buf)); 896170815Sariff} 897170815Sariff 898170815Sariffstatic int 899193640Sariffdsp_get_volume_channel(struct cdev *dev, struct pcm_channel **volch) 90050724Scg{ 901193640Sariff struct snddev_info *d; 902193640Sariff struct pcm_channel *c; 903193640Sariff int unit; 904193640Sariff 905193640Sariff KASSERT(dev != NULL && volch != NULL, 906193640Sariff ("%s(): NULL query dev=%p volch=%p", __func__, dev, volch)); 907193640Sariff 908193640Sariff d = dsp_get_info(dev); 909193640Sariff if (!PCM_REGISTERED(d)) { 910193640Sariff *volch = NULL; 911193640Sariff return (EINVAL); 912193640Sariff } 913193640Sariff 914193640Sariff PCM_UNLOCKASSERT(d); 915193640Sariff 916193640Sariff *volch = NULL; 917193640Sariff 918193640Sariff c = PCM_VOLCH(dev); 919193640Sariff if (c != NULL) { 920193640Sariff if (!(c->feederflags & (1 << FEEDER_VOLUME))) 921193640Sariff return (-1); 922193640Sariff *volch = c; 923193640Sariff return (0); 924193640Sariff } 925193640Sariff 926193640Sariff PCM_LOCK(d); 927193640Sariff PCM_WAIT(d); 928193640Sariff PCM_ACQUIRE(d); 929193640Sariff 930193640Sariff unit = dev2unit(dev); 931193640Sariff 932193640Sariff CHN_FOREACH(c, d, channels.pcm) { 933193640Sariff CHN_LOCK(c); 934193640Sariff if (c->unit != unit) { 935193640Sariff CHN_UNLOCK(c); 936193640Sariff continue; 937193640Sariff } 938193640Sariff *volch = c; 939193640Sariff pcm_chnref(c, 1); 940193640Sariff PCM_VOLCH(dev) = c; 941193640Sariff CHN_UNLOCK(c); 942193640Sariff PCM_RELEASE(d); 943193640Sariff PCM_UNLOCK(d); 944193640Sariff return ((c->feederflags & (1 << FEEDER_VOLUME)) ? 0 : -1); 945193640Sariff } 946193640Sariff 947193640Sariff PCM_RELEASE(d); 948193640Sariff PCM_UNLOCK(d); 949193640Sariff 950193640Sariff return (EINVAL); 951193640Sariff} 952193640Sariff 953193640Sariffstatic int 954193640Sariffdsp_ioctl_channel(struct cdev *dev, struct pcm_channel *volch, u_long cmd, 955193640Sariff caddr_t arg) 956193640Sariff{ 957193640Sariff struct snddev_info *d; 958193640Sariff struct pcm_channel *rdch, *wrch; 959193640Sariff int j, devtype, ret; 960193640Sariff 961193640Sariff d = dsp_get_info(dev); 962193640Sariff if (!PCM_REGISTERED(d) || !(dsp_get_flags(dev) & SD_F_VPC)) 963193640Sariff return (-1); 964193640Sariff 965193640Sariff PCM_UNLOCKASSERT(d); 966193640Sariff 967193640Sariff j = cmd & 0xff; 968193640Sariff 969193640Sariff rdch = PCM_RDCH(dev); 970193640Sariff wrch = PCM_WRCH(dev); 971193640Sariff 972193640Sariff /* No specific channel, look into cache */ 973193640Sariff if (volch == NULL) 974193640Sariff volch = PCM_VOLCH(dev); 975193640Sariff 976193640Sariff /* Look harder */ 977193640Sariff if (volch == NULL) { 978193640Sariff if (j == SOUND_MIXER_RECLEV && rdch != NULL) 979193640Sariff volch = rdch; 980193640Sariff else if (j == SOUND_MIXER_PCM && wrch != NULL) 981193640Sariff volch = wrch; 982193640Sariff } 983193640Sariff 984193640Sariff devtype = PCMDEV(dev); 985193640Sariff 986193640Sariff /* Look super harder */ 987193640Sariff if (volch == NULL && 988193640Sariff (devtype == SND_DEV_DSPHW_PLAY || devtype == SND_DEV_DSPHW_VPLAY || 989193640Sariff devtype == SND_DEV_DSPHW_REC || devtype == SND_DEV_DSPHW_VREC)) { 990193640Sariff ret = dsp_get_volume_channel(dev, &volch); 991193640Sariff if (ret != 0) 992193640Sariff return (ret); 993193640Sariff if (volch == NULL) 994193640Sariff return (EINVAL); 995193640Sariff } 996193640Sariff 997193640Sariff /* Final validation */ 998193640Sariff if (volch != NULL) { 999193640Sariff CHN_LOCK(volch); 1000193640Sariff if (!(volch->feederflags & (1 << FEEDER_VOLUME))) { 1001193640Sariff CHN_UNLOCK(volch); 1002193640Sariff return (-1); 1003193640Sariff } 1004193640Sariff if (volch->direction == PCMDIR_PLAY) 1005193640Sariff wrch = volch; 1006193640Sariff else 1007193640Sariff rdch = volch; 1008193640Sariff } 1009193640Sariff 1010193640Sariff ret = EINVAL; 1011193640Sariff 1012193640Sariff if (volch != NULL && 1013193640Sariff ((j == SOUND_MIXER_PCM && volch->direction == PCMDIR_PLAY) || 1014193640Sariff (j == SOUND_MIXER_RECLEV && volch->direction == PCMDIR_REC))) { 1015202150Smav if ((cmd & ~0xff) == MIXER_WRITE(0)) { 1016193640Sariff int left, right, center; 1017193640Sariff 1018193640Sariff left = *(int *)arg & 0x7f; 1019193640Sariff right = ((*(int *)arg) >> 8) & 0x7f; 1020193640Sariff center = (left + right) >> 1; 1021193640Sariff chn_setvolume_multi(volch, SND_VOL_C_PCM, left, right, 1022193640Sariff center); 1023202150Smav } else if ((cmd & ~0xff) == MIXER_READ(0)) { 1024193640Sariff *(int *)arg = CHN_GETVOLUME(volch, 1025193640Sariff SND_VOL_C_PCM, SND_CHN_T_FL); 1026193640Sariff *(int *)arg |= CHN_GETVOLUME(volch, 1027193640Sariff SND_VOL_C_PCM, SND_CHN_T_FR) << 8; 1028193640Sariff } 1029193640Sariff ret = 0; 1030193640Sariff } else if (rdch != NULL || wrch != NULL) { 1031193640Sariff switch (j) { 1032193640Sariff case SOUND_MIXER_DEVMASK: 1033193640Sariff case SOUND_MIXER_CAPS: 1034193640Sariff case SOUND_MIXER_STEREODEVS: 1035202150Smav if ((cmd & ~0xff) == MIXER_READ(0)) { 1036193640Sariff *(int *)arg = 0; 1037193640Sariff if (rdch != NULL) 1038193640Sariff *(int *)arg |= SOUND_MASK_RECLEV; 1039193640Sariff if (wrch != NULL) 1040193640Sariff *(int *)arg |= SOUND_MASK_PCM; 1041193640Sariff } 1042193640Sariff ret = 0; 1043193640Sariff break; 1044193640Sariff case SOUND_MIXER_RECMASK: 1045193640Sariff case SOUND_MIXER_RECSRC: 1046202150Smav if ((cmd & ~0xff) == MIXER_READ(0)) 1047193640Sariff *(int *)arg = 0; 1048193640Sariff ret = 0; 1049193640Sariff break; 1050193640Sariff default: 1051193640Sariff break; 1052193640Sariff } 1053193640Sariff } 1054193640Sariff 1055193640Sariff if (volch != NULL) 1056193640Sariff CHN_UNLOCK(volch); 1057193640Sariff 1058193640Sariff return (ret); 1059193640Sariff} 1060193640Sariff 1061193640Sariffstatic int 1062193640Sariffdsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, 1063193640Sariff struct thread *td) 1064193640Sariff{ 1065126366Struckman struct pcm_channel *chn, *rdch, *wrch; 106678362Scg struct snddev_info *d; 1067225505Savg u_long xcmd; 1068225505Savg int *arg_i, ret, tmp; 106950724Scg 1070170815Sariff d = dsp_get_info(i_dev); 1071170815Sariff if (!DSP_REGISTERED(d, i_dev)) 1072170815Sariff return (EBADF); 1073170815Sariff 1074170815Sariff PCM_GIANT_ENTER(d); 1075170815Sariff 1076170815Sariff arg_i = (int *)arg; 1077170815Sariff ret = 0; 1078162588Snetchild xcmd = 0; 1079193640Sariff chn = NULL; 1080162588Snetchild 1081156929Sariff if (IOCGROUP(cmd) == 'M') { 1082202170Smav if (cmd == OSS_GETVERSION) { 1083202170Smav *arg_i = SOUND_VERSION; 1084205734Smav PCM_GIANT_EXIT(d); 1085202170Smav return (0); 1086202170Smav } 1087193640Sariff ret = dsp_ioctl_channel(i_dev, PCM_VOLCH(i_dev), cmd, arg); 1088193640Sariff if (ret != -1) { 1089193640Sariff PCM_GIANT_EXIT(d); 1090193640Sariff return (ret); 1091193640Sariff } 1092193640Sariff 1093170815Sariff if (d->mixer_dev != NULL) { 1094170815Sariff PCM_ACQUIRE_QUICK(d); 1095170815Sariff ret = mixer_ioctl_cmd(d->mixer_dev, cmd, arg, -1, td, 1096170815Sariff MIXER_CMD_DIRECT); 1097170815Sariff PCM_RELEASE_QUICK(d); 1098170815Sariff } else 1099170815Sariff ret = EBADF; 1100170815Sariff 1101170815Sariff PCM_GIANT_EXIT(d); 1102170815Sariff 1103170815Sariff return (ret); 1104156929Sariff } 110578362Scg 1106162588Snetchild /* 1107162588Snetchild * Certain ioctls may be made on any type of device (audio, mixer, 1108162588Snetchild * and MIDI). Handle those special cases here. 1109162588Snetchild */ 1110162588Snetchild if (IOCGROUP(cmd) == 'X') { 1111170815Sariff PCM_ACQUIRE_QUICK(d); 1112162588Snetchild switch(cmd) { 1113162588Snetchild case SNDCTL_SYSINFO: 1114162588Snetchild sound_oss_sysinfo((oss_sysinfo *)arg); 1115162588Snetchild break; 1116187030Smav case SNDCTL_CARDINFO: 1117187030Smav ret = sound_oss_card_info((oss_card_info *)arg); 1118187030Smav break; 1119162588Snetchild case SNDCTL_AUDIOINFO: 1120187030Smav case SNDCTL_AUDIOINFO_EX: 1121187030Smav case SNDCTL_ENGINEINFO: 1122162588Snetchild ret = dsp_oss_audioinfo(i_dev, (oss_audioinfo *)arg); 1123162588Snetchild break; 1124162588Snetchild case SNDCTL_MIXERINFO: 1125162588Snetchild ret = mixer_oss_mixerinfo(i_dev, (oss_mixerinfo *)arg); 1126162588Snetchild break; 1127162588Snetchild default: 1128162588Snetchild ret = EINVAL; 1129162588Snetchild } 1130170815Sariff PCM_RELEASE_QUICK(d); 1131170815Sariff PCM_GIANT_EXIT(d); 1132170815Sariff return (ret); 1133162588Snetchild } 1134162588Snetchild 113578362Scg getchns(i_dev, &rdch, &wrch, 0); 113678362Scg 1137193640Sariff if (wrch != NULL && (wrch->flags & CHN_F_DEAD)) 113878362Scg wrch = NULL; 1139193640Sariff if (rdch != NULL && (rdch->flags & CHN_F_DEAD)) 114061886Scg rdch = NULL; 1141170161Sariff 1142170815Sariff if (wrch == NULL && rdch == NULL) { 1143170815Sariff PCM_GIANT_EXIT(d); 1144170815Sariff return (EINVAL); 1145170815Sariff } 1146170815Sariff 114750724Scg switch(cmd) { 114859246Scg#ifdef OLDPCM_IOCTL 114950724Scg /* 115050724Scg * we start with the new ioctl interface. 115150724Scg */ 115250724Scg case AIONWRITE: /* how many bytes can write ? */ 1153155008Snetchild if (wrch) { 1154155008Snetchild CHN_LOCK(wrch); 115574763Scg/* 115674763Scg if (wrch && wrch->bufhard.dl) 115774763Scg while (chn_wrfeed(wrch) == 0); 115874763Scg*/ 1159155008Snetchild *arg_i = sndbuf_getfree(wrch->bufsoft); 1160155008Snetchild CHN_UNLOCK(wrch); 1161155008Snetchild } else { 1162155008Snetchild *arg_i = 0; 1163155008Snetchild ret = EINVAL; 1164155008Snetchild } 116550724Scg break; 116650724Scg 116750724Scg case AIOSSIZE: /* set the current blocksize */ 116850724Scg { 116950724Scg struct snd_size *p = (struct snd_size *)arg; 117078214Scg 117178362Scg p->play_size = 0; 117278362Scg p->rec_size = 0; 1173170815Sariff PCM_ACQUIRE_QUICK(d); 117478362Scg if (wrch) { 1175126366Struckman CHN_LOCK(wrch); 117660976Scg chn_setblocksize(wrch, 2, p->play_size); 117778362Scg p->play_size = sndbuf_getblksz(wrch->bufsoft); 1178126366Struckman CHN_UNLOCK(wrch); 117978362Scg } 118078362Scg if (rdch) { 1181126366Struckman CHN_LOCK(rdch); 118260976Scg chn_setblocksize(rdch, 2, p->rec_size); 118378362Scg p->rec_size = sndbuf_getblksz(rdch->bufsoft); 1184126366Struckman CHN_UNLOCK(rdch); 118578362Scg } 1186170815Sariff PCM_RELEASE_QUICK(d); 118750724Scg } 118878362Scg break; 118950724Scg case AIOGSIZE: /* get the current blocksize */ 119050724Scg { 119150724Scg struct snd_size *p = (struct snd_size *)arg; 119278214Scg 1193126366Struckman if (wrch) { 1194126366Struckman CHN_LOCK(wrch); 119574763Scg p->play_size = sndbuf_getblksz(wrch->bufsoft); 1196126366Struckman CHN_UNLOCK(wrch); 1197126366Struckman } 1198126366Struckman if (rdch) { 1199126366Struckman CHN_LOCK(rdch); 120074763Scg p->rec_size = sndbuf_getblksz(rdch->bufsoft); 1201126366Struckman CHN_UNLOCK(rdch); 1202126366Struckman } 120350724Scg } 120450724Scg break; 120550724Scg 120650724Scg case AIOSFMT: 1207126366Struckman case AIOGFMT: 120850724Scg { 120950724Scg snd_chan_param *p = (snd_chan_param *)arg; 121078214Scg 1211151878Sdes if (cmd == AIOSFMT && 1212151878Sdes ((p->play_format != 0 && p->play_rate == 0) || 1213151878Sdes (p->rec_format != 0 && p->rec_rate == 0))) { 1214151878Sdes ret = EINVAL; 1215151878Sdes break; 1216151878Sdes } 1217170815Sariff PCM_ACQUIRE_QUICK(d); 121850724Scg if (wrch) { 1219126366Struckman CHN_LOCK(wrch); 1220151878Sdes if (cmd == AIOSFMT && p->play_format != 0) { 1221193640Sariff chn_setformat(wrch, 1222193640Sariff SND_FORMAT(p->play_format, 1223193640Sariff AFMT_CHANNEL(wrch->format), 1224193640Sariff AFMT_EXTCHANNEL(wrch->format))); 1225126366Struckman chn_setspeed(wrch, p->play_rate); 1226126366Struckman } 1227126366Struckman p->play_rate = wrch->speed; 1228193640Sariff p->play_format = AFMT_ENCODING(wrch->format); 1229126366Struckman CHN_UNLOCK(wrch); 1230126366Struckman } else { 1231126366Struckman p->play_rate = 0; 1232126366Struckman p->play_format = 0; 123350724Scg } 123450724Scg if (rdch) { 1235126366Struckman CHN_LOCK(rdch); 1236151878Sdes if (cmd == AIOSFMT && p->rec_format != 0) { 1237193640Sariff chn_setformat(rdch, 1238193640Sariff SND_FORMAT(p->rec_format, 1239193640Sariff AFMT_CHANNEL(rdch->format), 1240193640Sariff AFMT_EXTCHANNEL(rdch->format))); 1241126366Struckman chn_setspeed(rdch, p->rec_rate); 1242126366Struckman } 1243126366Struckman p->rec_rate = rdch->speed; 1244193640Sariff p->rec_format = AFMT_ENCODING(rdch->format); 1245126366Struckman CHN_UNLOCK(rdch); 1246126366Struckman } else { 1247126366Struckman p->rec_rate = 0; 1248126366Struckman p->rec_format = 0; 124950724Scg } 1250170815Sariff PCM_RELEASE_QUICK(d); 125150724Scg } 125250724Scg break; 125350724Scg 125450724Scg case AIOGCAP: /* get capabilities */ 125550724Scg { 125650724Scg snd_capabilities *p = (snd_capabilities *)arg; 125774763Scg struct pcmchan_caps *pcaps = NULL, *rcaps = NULL; 1258130585Sphk struct cdev *pdev; 125978214Scg 1260193640Sariff PCM_LOCK(d); 1261126366Struckman if (rdch) { 1262126366Struckman CHN_LOCK(rdch); 126378214Scg rcaps = chn_getcaps(rdch); 1264126366Struckman } 1265126366Struckman if (wrch) { 1266126366Struckman CHN_LOCK(wrch); 126778214Scg pcaps = chn_getcaps(wrch); 1268126366Struckman } 126950724Scg p->rate_min = max(rcaps? rcaps->minspeed : 0, 127050724Scg pcaps? pcaps->minspeed : 0); 127150724Scg p->rate_max = min(rcaps? rcaps->maxspeed : 1000000, 127250724Scg pcaps? pcaps->maxspeed : 1000000); 127374763Scg p->bufsize = min(rdch? sndbuf_getsize(rdch->bufsoft) : 1000000, 127474763Scg wrch? sndbuf_getsize(wrch->bufsoft) : 1000000); 127550724Scg /* XXX bad on sb16 */ 127664881Scg p->formats = (rdch? chn_getformats(rdch) : 0xffffffff) & 127764881Scg (wrch? chn_getformats(wrch) : 0xffffffff); 127861361Scg if (rdch && wrch) 127982180Scg p->formats |= (dsp_get_flags(i_dev) & SD_F_SIMPLEX)? 0 : AFMT_FULLDUPLEX; 1280124617Sphk pdev = d->mixer_dev; 128150724Scg p->mixers = 1; /* default: one mixer */ 128278362Scg p->inputs = pdev->si_drv1? mix_getdevs(pdev->si_drv1) : 0; 128350724Scg p->left = p->right = 100; 1284170815Sariff if (wrch) 1285170815Sariff CHN_UNLOCK(wrch); 1286126366Struckman if (rdch) 1287126366Struckman CHN_UNLOCK(rdch); 1288193640Sariff PCM_UNLOCK(d); 128950724Scg } 129050724Scg break; 129150724Scg 129250724Scg case AIOSTOP: 1293126366Struckman if (*arg_i == AIOSYNC_PLAY && wrch) { 1294126366Struckman CHN_LOCK(wrch); 129570291Scg *arg_i = chn_abort(wrch); 1296126366Struckman CHN_UNLOCK(wrch); 1297126366Struckman } else if (*arg_i == AIOSYNC_CAPTURE && rdch) { 1298126366Struckman CHN_LOCK(rdch); 129970291Scg *arg_i = chn_abort(rdch); 1300126366Struckman CHN_UNLOCK(rdch); 1301126366Struckman } else { 130250724Scg printf("AIOSTOP: bad channel 0x%x\n", *arg_i); 130350724Scg *arg_i = 0; 130450724Scg } 130550724Scg break; 130650724Scg 130750724Scg case AIOSYNC: 130850724Scg printf("AIOSYNC chan 0x%03lx pos %lu unimplemented\n", 130950724Scg ((snd_sync_parm *)arg)->chan, ((snd_sync_parm *)arg)->pos); 131050724Scg break; 131159577Scg#endif 131251407Speter /* 131351407Speter * here follow the standard ioctls (filio.h etc.) 131451407Speter */ 131550724Scg case FIONREAD: /* get # bytes to read */ 1316126366Struckman if (rdch) { 1317126366Struckman CHN_LOCK(rdch); 1318126366Struckman/* if (rdch && rdch->bufhard.dl) 1319126366Struckman while (chn_rdfeed(rdch) == 0); 1320126366Struckman*/ 1321126366Struckman *arg_i = sndbuf_getready(rdch->bufsoft); 1322126366Struckman CHN_UNLOCK(rdch); 1323155008Snetchild } else { 1324126366Struckman *arg_i = 0; 1325155008Snetchild ret = EINVAL; 1326155008Snetchild } 132750724Scg break; 132850724Scg 132950724Scg case FIOASYNC: /*set/clear async i/o */ 133050724Scg DEB( printf("FIOASYNC\n") ; ) 133150724Scg break; 133259577Scg 1333162775Sru case SNDCTL_DSP_NONBLOCK: /* set non-blocking i/o */ 133450724Scg case FIONBIO: /* set/clear non-blocking i/o */ 1335126366Struckman if (rdch) { 1336126366Struckman CHN_LOCK(rdch); 1337162775Sru if (cmd == SNDCTL_DSP_NONBLOCK || *arg_i) 133878214Scg rdch->flags |= CHN_F_NBIO; 1339126366Struckman else 1340126366Struckman rdch->flags &= ~CHN_F_NBIO; 1341126366Struckman CHN_UNLOCK(rdch); 1342126366Struckman } 1343126366Struckman if (wrch) { 1344126366Struckman CHN_LOCK(wrch); 1345162775Sru if (cmd == SNDCTL_DSP_NONBLOCK || *arg_i) 134678214Scg wrch->flags |= CHN_F_NBIO; 1347126366Struckman else 1348126366Struckman wrch->flags &= ~CHN_F_NBIO; 1349126366Struckman CHN_UNLOCK(wrch); 135050724Scg } 135150724Scg break; 135250724Scg 135350724Scg /* 135451407Speter * Finally, here is the linux-compatible ioctl interface 135551407Speter */ 135651407Speter#define THE_REAL_SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int) 135750724Scg case THE_REAL_SNDCTL_DSP_GETBLKSIZE: 135850724Scg case SNDCTL_DSP_GETBLKSIZE: 1359126366Struckman chn = wrch ? wrch : rdch; 1360155008Snetchild if (chn) { 1361155008Snetchild CHN_LOCK(chn); 1362155008Snetchild *arg_i = sndbuf_getblksz(chn->bufsoft); 1363155008Snetchild CHN_UNLOCK(chn); 1364155008Snetchild } else { 1365155008Snetchild *arg_i = 0; 1366155008Snetchild ret = EINVAL; 1367155008Snetchild } 1368170815Sariff break; 136950724Scg 137050724Scg case SNDCTL_DSP_SETBLKSIZE: 137159246Scg RANGE(*arg_i, 16, 65536); 1372170815Sariff PCM_ACQUIRE_QUICK(d); 1373126366Struckman if (wrch) { 1374126366Struckman CHN_LOCK(wrch); 137578214Scg chn_setblocksize(wrch, 2, *arg_i); 1376126366Struckman CHN_UNLOCK(wrch); 1377126366Struckman } 1378126366Struckman if (rdch) { 1379126366Struckman CHN_LOCK(rdch); 138078214Scg chn_setblocksize(rdch, 2, *arg_i); 1381126366Struckman CHN_UNLOCK(rdch); 1382126366Struckman } 1383170815Sariff PCM_RELEASE_QUICK(d); 138450724Scg break; 138550724Scg 138650724Scg case SNDCTL_DSP_RESET: 138750724Scg DEB(printf("dsp reset\n")); 1388124743Smatk if (wrch) { 1389126366Struckman CHN_LOCK(wrch); 139074763Scg chn_abort(wrch); 1391124743Smatk chn_resetbuf(wrch); 1392126366Struckman CHN_UNLOCK(wrch); 1393124743Smatk } 1394124743Smatk if (rdch) { 1395126366Struckman CHN_LOCK(rdch); 139674763Scg chn_abort(rdch); 1397124743Smatk chn_resetbuf(rdch); 1398126366Struckman CHN_UNLOCK(rdch); 1399124743Smatk } 140050724Scg break; 140150724Scg 140250724Scg case SNDCTL_DSP_SYNC: 140351407Speter DEB(printf("dsp sync\n")); 140478362Scg /* chn_sync may sleep */ 1405126366Struckman if (wrch) { 1406126366Struckman CHN_LOCK(wrch); 1407164614Sariff chn_sync(wrch, 0); 1408126366Struckman CHN_UNLOCK(wrch); 1409171204Sariff } 141050724Scg break; 141150724Scg 141250724Scg case SNDCTL_DSP_SPEED: 141378362Scg /* chn_setspeed may sleep */ 141478362Scg tmp = 0; 1415170815Sariff PCM_ACQUIRE_QUICK(d); 141678362Scg if (wrch) { 1417126366Struckman CHN_LOCK(wrch); 141860961Scg ret = chn_setspeed(wrch, *arg_i); 141978362Scg tmp = wrch->speed; 1420126366Struckman CHN_UNLOCK(wrch); 142178362Scg } 142278362Scg if (rdch && ret == 0) { 1423126366Struckman CHN_LOCK(rdch); 142460961Scg ret = chn_setspeed(rdch, *arg_i); 142578362Scg if (tmp == 0) 142678362Scg tmp = rdch->speed; 1427126366Struckman CHN_UNLOCK(rdch); 142878362Scg } 1429170815Sariff PCM_RELEASE_QUICK(d); 143078362Scg *arg_i = tmp; 143178362Scg break; 143250724Scg 143350724Scg case SOUND_PCM_READ_RATE: 1434126366Struckman chn = wrch ? wrch : rdch; 1435155008Snetchild if (chn) { 1436155008Snetchild CHN_LOCK(chn); 1437155008Snetchild *arg_i = chn->speed; 1438155008Snetchild CHN_UNLOCK(chn); 1439155008Snetchild } else { 1440155008Snetchild *arg_i = 0; 1441155008Snetchild ret = EINVAL; 1442155008Snetchild } 144350724Scg break; 144450724Scg 144550724Scg case SNDCTL_DSP_STEREO: 144678362Scg tmp = -1; 1447193640Sariff *arg_i = (*arg_i)? 2 : 1; 1448170815Sariff PCM_ACQUIRE_QUICK(d); 144978362Scg if (wrch) { 1450126366Struckman CHN_LOCK(wrch); 1451193640Sariff ret = chn_setformat(wrch, 1452193640Sariff SND_FORMAT(wrch->format, *arg_i, 0)); 1453193640Sariff tmp = (AFMT_CHANNEL(wrch->format) > 1)? 1 : 0; 1454126366Struckman CHN_UNLOCK(wrch); 145578362Scg } 145678362Scg if (rdch && ret == 0) { 1457126366Struckman CHN_LOCK(rdch); 1458193640Sariff ret = chn_setformat(rdch, 1459193640Sariff SND_FORMAT(rdch->format, *arg_i, 0)); 146078362Scg if (tmp == -1) 1461193640Sariff tmp = (AFMT_CHANNEL(rdch->format) > 1)? 1 : 0; 1462126366Struckman CHN_UNLOCK(rdch); 146378362Scg } 1464170815Sariff PCM_RELEASE_QUICK(d); 146578362Scg *arg_i = tmp; 146650724Scg break; 146750724Scg 146850724Scg case SOUND_PCM_WRITE_CHANNELS: 146960961Scg/* case SNDCTL_DSP_CHANNELS: ( == SOUND_PCM_WRITE_CHANNELS) */ 1470193640Sariff if (*arg_i < 0) { 1471193640Sariff *arg_i = 0; 1472193640Sariff ret = EINVAL; 1473193640Sariff break; 1474193640Sariff } 147582185Scg if (*arg_i != 0) { 1476193640Sariff struct pcmchan_matrix *m; 1477193640Sariff uint32_t ext; 1478193640Sariff 147978362Scg tmp = 0; 1480193640Sariff if (*arg_i > SND_CHN_MAX) 1481193640Sariff *arg_i = SND_CHN_MAX; 1482193640Sariff 1483193640Sariff m = feeder_matrix_default_channel_map(*arg_i); 1484193640Sariff if (m != NULL) 1485193640Sariff ext = m->ext; 1486193640Sariff else 1487193640Sariff ext = 0; 1488193640Sariff 1489170815Sariff PCM_ACQUIRE_QUICK(d); 149078362Scg if (wrch) { 1491126366Struckman CHN_LOCK(wrch); 1492193640Sariff ret = chn_setformat(wrch, 1493193640Sariff SND_FORMAT(wrch->format, *arg_i, ext)); 1494193640Sariff tmp = AFMT_CHANNEL(wrch->format); 1495126366Struckman CHN_UNLOCK(wrch); 149678362Scg } 149778362Scg if (rdch && ret == 0) { 1498126366Struckman CHN_LOCK(rdch); 1499193640Sariff ret = chn_setformat(rdch, 1500193640Sariff SND_FORMAT(rdch->format, *arg_i, ext)); 150178362Scg if (tmp == 0) 1502193640Sariff tmp = AFMT_CHANNEL(rdch->format); 1503126366Struckman CHN_UNLOCK(rdch); 150478362Scg } 1505170815Sariff PCM_RELEASE_QUICK(d); 150678362Scg *arg_i = tmp; 1507126366Struckman } else { 1508126366Struckman chn = wrch ? wrch : rdch; 1509126366Struckman CHN_LOCK(chn); 1510193640Sariff *arg_i = AFMT_CHANNEL(chn->format); 1511126366Struckman CHN_UNLOCK(chn); 1512126366Struckman } 151360961Scg break; 151450724Scg 151550724Scg case SOUND_PCM_READ_CHANNELS: 1516126366Struckman chn = wrch ? wrch : rdch; 1517155008Snetchild if (chn) { 1518155008Snetchild CHN_LOCK(chn); 1519193640Sariff *arg_i = AFMT_CHANNEL(chn->format); 1520155008Snetchild CHN_UNLOCK(chn); 1521155008Snetchild } else { 1522155008Snetchild *arg_i = 0; 1523155008Snetchild ret = EINVAL; 1524155008Snetchild } 152550724Scg break; 152650724Scg 152750724Scg case SNDCTL_DSP_GETFMTS: /* returns a mask of supported fmts */ 1528126366Struckman chn = wrch ? wrch : rdch; 1529155008Snetchild if (chn) { 1530155008Snetchild CHN_LOCK(chn); 1531155008Snetchild *arg_i = chn_getformats(chn); 1532155008Snetchild CHN_UNLOCK(chn); 1533155008Snetchild } else { 1534155008Snetchild *arg_i = 0; 1535155008Snetchild ret = EINVAL; 1536155008Snetchild } 1537170815Sariff break; 153850724Scg 153950724Scg case SNDCTL_DSP_SETFMT: /* sets _one_ format */ 1540193640Sariff if (*arg_i != AFMT_QUERY) { 154178362Scg tmp = 0; 1542170815Sariff PCM_ACQUIRE_QUICK(d); 154378362Scg if (wrch) { 1544126366Struckman CHN_LOCK(wrch); 1545193640Sariff ret = chn_setformat(wrch, SND_FORMAT(*arg_i, 1546193640Sariff AFMT_CHANNEL(wrch->format), 1547193640Sariff AFMT_EXTCHANNEL(wrch->format))); 1548193640Sariff tmp = wrch->format; 1549126366Struckman CHN_UNLOCK(wrch); 155078362Scg } 155178362Scg if (rdch && ret == 0) { 1552126366Struckman CHN_LOCK(rdch); 1553193640Sariff ret = chn_setformat(rdch, SND_FORMAT(*arg_i, 1554193640Sariff AFMT_CHANNEL(rdch->format), 1555193640Sariff AFMT_EXTCHANNEL(rdch->format))); 155678362Scg if (tmp == 0) 1557193640Sariff tmp = rdch->format; 1558126366Struckman CHN_UNLOCK(rdch); 155978362Scg } 1560170815Sariff PCM_RELEASE_QUICK(d); 1561193640Sariff *arg_i = AFMT_ENCODING(tmp); 1562126366Struckman } else { 1563126366Struckman chn = wrch ? wrch : rdch; 1564126366Struckman CHN_LOCK(chn); 1565193640Sariff *arg_i = AFMT_ENCODING(chn->format); 1566126366Struckman CHN_UNLOCK(chn); 1567126366Struckman } 156850724Scg break; 156950724Scg 157050724Scg case SNDCTL_DSP_SETFRAGMENT: 157150724Scg DEB(printf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg)); 157250724Scg { 1573170815Sariff uint32_t fragln = (*arg_i) & 0x0000ffff; 1574170815Sariff uint32_t maxfrags = ((*arg_i) & 0xffff0000) >> 16; 1575170815Sariff uint32_t fragsz; 1576170815Sariff uint32_t r_maxfrags, r_fragsz; 157759246Scg 157859246Scg RANGE(fragln, 4, 16); 157959246Scg fragsz = 1 << fragln; 158059246Scg 158159246Scg if (maxfrags == 0) 158259246Scg maxfrags = CHN_2NDBUFMAXSIZE / fragsz; 1583116881Smdodd if (maxfrags < 2) 1584116881Smdodd maxfrags = 2; 158559246Scg if (maxfrags * fragsz > CHN_2NDBUFMAXSIZE) 158659246Scg maxfrags = CHN_2NDBUFMAXSIZE / fragsz; 158750724Scg 158859577Scg DEB(printf("SNDCTL_DSP_SETFRAGMENT %d frags, %d sz\n", maxfrags, fragsz)); 1589170815Sariff PCM_ACQUIRE_QUICK(d); 159078362Scg if (rdch) { 1591126366Struckman CHN_LOCK(rdch); 159259246Scg ret = chn_setblocksize(rdch, maxfrags, fragsz); 1593152424Sariff r_maxfrags = sndbuf_getblkcnt(rdch->bufsoft); 1594152424Sariff r_fragsz = sndbuf_getblksz(rdch->bufsoft); 1595126366Struckman CHN_UNLOCK(rdch); 1596152424Sariff } else { 1597152424Sariff r_maxfrags = maxfrags; 1598152424Sariff r_fragsz = fragsz; 159978362Scg } 160078362Scg if (wrch && ret == 0) { 1601126366Struckman CHN_LOCK(wrch); 160259246Scg ret = chn_setblocksize(wrch, maxfrags, fragsz); 160378362Scg maxfrags = sndbuf_getblkcnt(wrch->bufsoft); 160478362Scg fragsz = sndbuf_getblksz(wrch->bufsoft); 1605126366Struckman CHN_UNLOCK(wrch); 1606152424Sariff } else { /* use whatever came from the read channel */ 1607152424Sariff maxfrags = r_maxfrags; 1608152424Sariff fragsz = r_fragsz; 160978362Scg } 1610170815Sariff PCM_RELEASE_QUICK(d); 161159246Scg 161260961Scg fragln = 0; 161360961Scg while (fragsz > 1) { 161460961Scg fragln++; 161560961Scg fragsz >>= 1; 161660961Scg } 161778362Scg *arg_i = (maxfrags << 16) | fragln; 161850724Scg } 161950724Scg break; 162050724Scg 162185407Scg case SNDCTL_DSP_GETISPACE: 162255204Scg /* return the size of data available in the input queue */ 162350724Scg { 162450724Scg audio_buf_info *a = (audio_buf_info *)arg; 162550724Scg if (rdch) { 162674763Scg struct snd_dbuf *bs = rdch->bufsoft; 162774763Scg 1628126366Struckman CHN_LOCK(rdch); 162985407Scg a->bytes = sndbuf_getready(bs); 163074763Scg a->fragments = a->bytes / sndbuf_getblksz(bs); 163174763Scg a->fragstotal = sndbuf_getblkcnt(bs); 163274763Scg a->fragsize = sndbuf_getblksz(bs); 1633126366Struckman CHN_UNLOCK(rdch); 1634170815Sariff } else 1635170815Sariff ret = EINVAL; 163650724Scg } 163750724Scg break; 163850724Scg 163950724Scg case SNDCTL_DSP_GETOSPACE: 164050724Scg /* return space available in the output queue */ 164150724Scg { 164250724Scg audio_buf_info *a = (audio_buf_info *)arg; 164350724Scg if (wrch) { 164474763Scg struct snd_dbuf *bs = wrch->bufsoft; 164574763Scg 1646126366Struckman CHN_LOCK(wrch); 1647153869Sariff /* XXX abusive DMA update: chn_wrupdate(wrch); */ 164874763Scg a->bytes = sndbuf_getfree(bs); 164974763Scg a->fragments = a->bytes / sndbuf_getblksz(bs); 165074763Scg a->fragstotal = sndbuf_getblkcnt(bs); 165174763Scg a->fragsize = sndbuf_getblksz(bs); 1652126366Struckman CHN_UNLOCK(wrch); 1653170815Sariff } else 1654170815Sariff ret = EINVAL; 165550724Scg } 165650724Scg break; 165750724Scg 165850724Scg case SNDCTL_DSP_GETIPTR: 165950724Scg { 166050724Scg count_info *a = (count_info *)arg; 166150724Scg if (rdch) { 166274763Scg struct snd_dbuf *bs = rdch->bufsoft; 166374763Scg 1664126366Struckman CHN_LOCK(rdch); 1665153869Sariff /* XXX abusive DMA update: chn_rdupdate(rdch); */ 166674763Scg a->bytes = sndbuf_gettotal(bs); 166774763Scg a->blocks = sndbuf_getblocks(bs) - rdch->blocks; 1668221388Savg a->ptr = sndbuf_getfreeptr(bs); 166974763Scg rdch->blocks = sndbuf_getblocks(bs); 1670126366Struckman CHN_UNLOCK(rdch); 167178214Scg } else 167278214Scg ret = EINVAL; 167350724Scg } 167450724Scg break; 167550724Scg 167650724Scg case SNDCTL_DSP_GETOPTR: 167750724Scg { 167850724Scg count_info *a = (count_info *)arg; 167950724Scg if (wrch) { 168074763Scg struct snd_dbuf *bs = wrch->bufsoft; 168174763Scg 1682126366Struckman CHN_LOCK(wrch); 1683153869Sariff /* XXX abusive DMA update: chn_wrupdate(wrch); */ 168474763Scg a->bytes = sndbuf_gettotal(bs); 168574763Scg a->blocks = sndbuf_getblocks(bs) - wrch->blocks; 168674763Scg a->ptr = sndbuf_getreadyptr(bs); 168774763Scg wrch->blocks = sndbuf_getblocks(bs); 1688126366Struckman CHN_UNLOCK(wrch); 168978214Scg } else 169078214Scg ret = EINVAL; 169150724Scg } 169250724Scg break; 169350724Scg 169450724Scg case SNDCTL_DSP_GETCAPS: 1695193640Sariff PCM_LOCK(d); 1696187030Smav *arg_i = PCM_CAP_REALTIME | PCM_CAP_MMAP | PCM_CAP_TRIGGER; 169782180Scg if (rdch && wrch && !(dsp_get_flags(i_dev) & SD_F_SIMPLEX)) 1698187030Smav *arg_i |= PCM_CAP_DUPLEX; 1699193640Sariff PCM_UNLOCK(d); 170050724Scg break; 170150724Scg 170250724Scg case SOUND_PCM_READ_BITS: 1703126366Struckman chn = wrch ? wrch : rdch; 1704155008Snetchild if (chn) { 1705155008Snetchild CHN_LOCK(chn); 1706155008Snetchild if (chn->format & AFMT_8BIT) 1707155008Snetchild *arg_i = 8; 1708155008Snetchild else if (chn->format & AFMT_16BIT) 1709155008Snetchild *arg_i = 16; 1710155008Snetchild else if (chn->format & AFMT_24BIT) 1711155008Snetchild *arg_i = 24; 1712155008Snetchild else if (chn->format & AFMT_32BIT) 1713155008Snetchild *arg_i = 32; 1714155008Snetchild else 1715155008Snetchild ret = EINVAL; 1716155008Snetchild CHN_UNLOCK(chn); 1717155008Snetchild } else { 1718155008Snetchild *arg_i = 0; 1719149951Snetchild ret = EINVAL; 1720155008Snetchild } 172150724Scg break; 172250724Scg 172350724Scg case SNDCTL_DSP_SETTRIGGER: 172450724Scg if (rdch) { 1725126366Struckman CHN_LOCK(rdch); 1726178121Sariff rdch->flags &= ~CHN_F_NOTRIGGER; 172782182Scg if (*arg_i & PCM_ENABLE_INPUT) 172874763Scg chn_start(rdch, 1); 1729178121Sariff else { 1730178121Sariff chn_abort(rdch); 1731178121Sariff chn_resetbuf(rdch); 173259577Scg rdch->flags |= CHN_F_NOTRIGGER; 1733178121Sariff } 1734126366Struckman CHN_UNLOCK(rdch); 173550724Scg } 173650724Scg if (wrch) { 1737126366Struckman CHN_LOCK(wrch); 1738178121Sariff wrch->flags &= ~CHN_F_NOTRIGGER; 173982182Scg if (*arg_i & PCM_ENABLE_OUTPUT) 174074763Scg chn_start(wrch, 1); 1741178121Sariff else { 1742178121Sariff chn_abort(wrch); 1743178121Sariff chn_resetbuf(wrch); 174459577Scg wrch->flags |= CHN_F_NOTRIGGER; 1745178121Sariff } 1746126366Struckman CHN_UNLOCK(wrch); 174750724Scg } 174850724Scg break; 174950724Scg 175050724Scg case SNDCTL_DSP_GETTRIGGER: 175150724Scg *arg_i = 0; 1752126366Struckman if (wrch) { 1753126366Struckman CHN_LOCK(wrch); 1754126366Struckman if (wrch->flags & CHN_F_TRIGGERED) 1755126366Struckman *arg_i |= PCM_ENABLE_OUTPUT; 1756126366Struckman CHN_UNLOCK(wrch); 1757126366Struckman } 1758126366Struckman if (rdch) { 1759126366Struckman CHN_LOCK(rdch); 1760126366Struckman if (rdch->flags & CHN_F_TRIGGERED) 1761126366Struckman *arg_i |= PCM_ENABLE_INPUT; 1762126366Struckman CHN_UNLOCK(rdch); 1763126366Struckman } 176450724Scg break; 176550724Scg 176655204Scg case SNDCTL_DSP_GETODELAY: 176755204Scg if (wrch) { 176874763Scg struct snd_dbuf *bs = wrch->bufsoft; 176974763Scg 1770126366Struckman CHN_LOCK(wrch); 1771153869Sariff /* XXX abusive DMA update: chn_wrupdate(wrch); */ 1772164614Sariff *arg_i = sndbuf_getready(bs); 1773126366Struckman CHN_UNLOCK(wrch); 177455204Scg } else 177555204Scg ret = EINVAL; 177655204Scg break; 177767738Scg 177867598Scg case SNDCTL_DSP_POST: 177967598Scg if (wrch) { 1780126366Struckman CHN_LOCK(wrch); 178167598Scg wrch->flags &= ~CHN_F_NOTRIGGER; 178267598Scg chn_start(wrch, 1); 1783126366Struckman CHN_UNLOCK(wrch); 1784171204Sariff } 178567598Scg break; 178667738Scg 1787131461Snetchild case SNDCTL_DSP_SETDUPLEX: 1788131461Snetchild /* 1789131461Snetchild * switch to full-duplex mode if card is in half-duplex 1790131461Snetchild * mode and is able to work in full-duplex mode 1791131461Snetchild */ 1792193640Sariff PCM_LOCK(d); 1793131461Snetchild if (rdch && wrch && (dsp_get_flags(i_dev) & SD_F_SIMPLEX)) 1794131461Snetchild dsp_set_flags(i_dev, dsp_get_flags(i_dev)^SD_F_SIMPLEX); 1795193640Sariff PCM_UNLOCK(d); 1796131461Snetchild break; 1797131461Snetchild 1798162588Snetchild /* 1799162588Snetchild * The following four ioctls are simple wrappers around mixer_ioctl 1800162588Snetchild * with no further processing. xcmd is short for "translated 1801162588Snetchild * command". 1802162588Snetchild */ 1803162588Snetchild case SNDCTL_DSP_GETRECVOL: 1804193640Sariff if (xcmd == 0) { 1805162588Snetchild xcmd = SOUND_MIXER_READ_RECLEV; 1806193640Sariff chn = rdch; 1807193640Sariff } 1808162588Snetchild /* FALLTHROUGH */ 1809162588Snetchild case SNDCTL_DSP_SETRECVOL: 1810193640Sariff if (xcmd == 0) { 1811162588Snetchild xcmd = SOUND_MIXER_WRITE_RECLEV; 1812193640Sariff chn = rdch; 1813193640Sariff } 1814162588Snetchild /* FALLTHROUGH */ 1815162588Snetchild case SNDCTL_DSP_GETPLAYVOL: 1816193640Sariff if (xcmd == 0) { 1817162588Snetchild xcmd = SOUND_MIXER_READ_PCM; 1818193640Sariff chn = wrch; 1819193640Sariff } 1820162588Snetchild /* FALLTHROUGH */ 1821162588Snetchild case SNDCTL_DSP_SETPLAYVOL: 1822193640Sariff if (xcmd == 0) { 1823162588Snetchild xcmd = SOUND_MIXER_WRITE_PCM; 1824193640Sariff chn = wrch; 1825193640Sariff } 1826162588Snetchild 1827193640Sariff ret = dsp_ioctl_channel(i_dev, chn, xcmd, arg); 1828193640Sariff if (ret != -1) { 1829193640Sariff PCM_GIANT_EXIT(d); 1830193640Sariff return (ret); 1831193640Sariff } 1832193640Sariff 1833170815Sariff if (d->mixer_dev != NULL) { 1834170815Sariff PCM_ACQUIRE_QUICK(d); 1835170815Sariff ret = mixer_ioctl_cmd(d->mixer_dev, xcmd, arg, -1, td, 1836170815Sariff MIXER_CMD_DIRECT); 1837170815Sariff PCM_RELEASE_QUICK(d); 1838170815Sariff } else 1839162588Snetchild ret = ENOTSUP; 1840193640Sariff 1841162588Snetchild break; 1842162588Snetchild 1843162588Snetchild case SNDCTL_DSP_GET_RECSRC_NAMES: 1844162588Snetchild case SNDCTL_DSP_GET_RECSRC: 1845162588Snetchild case SNDCTL_DSP_SET_RECSRC: 1846170815Sariff if (d->mixer_dev != NULL) { 1847170815Sariff PCM_ACQUIRE_QUICK(d); 1848170815Sariff ret = mixer_ioctl_cmd(d->mixer_dev, cmd, arg, -1, td, 1849170815Sariff MIXER_CMD_DIRECT); 1850170815Sariff PCM_RELEASE_QUICK(d); 1851170815Sariff } else 1852162588Snetchild ret = ENOTSUP; 1853162588Snetchild break; 1854162588Snetchild 1855162588Snetchild /* 1856162588Snetchild * The following 3 ioctls aren't very useful at the moment. For 1857162588Snetchild * now, only a single channel is associated with a cdev (/dev/dspN 1858162588Snetchild * instance), so there's only a single output routing to use (i.e., 1859162588Snetchild * the wrch bound to this cdev). 1860162588Snetchild */ 1861162588Snetchild case SNDCTL_DSP_GET_PLAYTGT_NAMES: 1862162588Snetchild { 1863162588Snetchild oss_mixer_enuminfo *ei; 1864162588Snetchild ei = (oss_mixer_enuminfo *)arg; 1865162588Snetchild ei->dev = 0; 1866162588Snetchild ei->ctrl = 0; 1867162588Snetchild ei->version = 0; /* static for now */ 1868162588Snetchild ei->strindex[0] = 0; 1869162588Snetchild 1870162588Snetchild if (wrch != NULL) { 1871162588Snetchild ei->nvalues = 1; 1872162588Snetchild strlcpy(ei->strings, wrch->name, 1873162588Snetchild sizeof(ei->strings)); 1874162588Snetchild } else { 1875162588Snetchild ei->nvalues = 0; 1876162588Snetchild ei->strings[0] = '\0'; 1877162588Snetchild } 1878162588Snetchild } 1879162588Snetchild break; 1880162588Snetchild case SNDCTL_DSP_GET_PLAYTGT: 1881162588Snetchild case SNDCTL_DSP_SET_PLAYTGT: /* yes, they are the same for now */ 1882162588Snetchild /* 1883162588Snetchild * Re: SET_PLAYTGT 1884162588Snetchild * OSSv4: "The value that was accepted by the device will 1885162588Snetchild * be returned back in the variable pointed by the 1886162588Snetchild * argument." 1887162588Snetchild */ 1888162588Snetchild if (wrch != NULL) 1889162588Snetchild *arg_i = 0; 1890162588Snetchild else 1891162588Snetchild ret = EINVAL; 1892162588Snetchild break; 1893162588Snetchild 1894162588Snetchild case SNDCTL_DSP_SILENCE: 1895162588Snetchild /* 1896162588Snetchild * Flush the software (pre-feed) buffer, but try to minimize playback 1897162588Snetchild * interruption. (I.e., record unplayed samples with intent to 1898162588Snetchild * restore by SNDCTL_DSP_SKIP.) Intended for application "pause" 1899162588Snetchild * functionality. 1900162588Snetchild */ 1901162588Snetchild if (wrch == NULL) 1902162588Snetchild ret = EINVAL; 1903162588Snetchild else { 1904162588Snetchild struct snd_dbuf *bs; 1905162588Snetchild CHN_LOCK(wrch); 1906162588Snetchild while (wrch->inprog != 0) 1907162588Snetchild cv_wait(&wrch->cv, wrch->lock); 1908162588Snetchild bs = wrch->bufsoft; 1909162588Snetchild if ((bs->shadbuf != NULL) && (sndbuf_getready(bs) > 0)) { 1910162588Snetchild bs->sl = sndbuf_getready(bs); 1911162588Snetchild sndbuf_dispose(bs, bs->shadbuf, sndbuf_getready(bs)); 1912162588Snetchild sndbuf_fillsilence(bs); 1913162588Snetchild chn_start(wrch, 0); 1914162588Snetchild } 1915162588Snetchild CHN_UNLOCK(wrch); 1916162588Snetchild } 1917162588Snetchild break; 1918162588Snetchild 1919162588Snetchild case SNDCTL_DSP_SKIP: 1920162588Snetchild /* 1921162588Snetchild * OSSv4 docs: "This ioctl call discards all unplayed samples in the 1922162588Snetchild * playback buffer by moving the current write position immediately 1923162588Snetchild * before the point where the device is currently reading the samples." 1924162588Snetchild */ 1925162588Snetchild if (wrch == NULL) 1926162588Snetchild ret = EINVAL; 1927162588Snetchild else { 1928162588Snetchild struct snd_dbuf *bs; 1929162588Snetchild CHN_LOCK(wrch); 1930162588Snetchild while (wrch->inprog != 0) 1931162588Snetchild cv_wait(&wrch->cv, wrch->lock); 1932162588Snetchild bs = wrch->bufsoft; 1933162588Snetchild if ((bs->shadbuf != NULL) && (bs->sl > 0)) { 1934162588Snetchild sndbuf_softreset(bs); 1935162588Snetchild sndbuf_acquire(bs, bs->shadbuf, bs->sl); 1936162588Snetchild bs->sl = 0; 1937162588Snetchild chn_start(wrch, 0); 1938162588Snetchild } 1939162588Snetchild CHN_UNLOCK(wrch); 1940162588Snetchild } 1941162588Snetchild break; 1942162588Snetchild 1943162588Snetchild case SNDCTL_DSP_CURRENT_OPTR: 1944162588Snetchild case SNDCTL_DSP_CURRENT_IPTR: 1945162588Snetchild /** 1946162588Snetchild * @note Changing formats resets the buffer counters, which differs 1947162588Snetchild * from the 4Front drivers. However, I don't expect this to be 1948162588Snetchild * much of a problem. 1949162588Snetchild * 1950162588Snetchild * @note In a test where @c CURRENT_OPTR is called immediately after write 1951162588Snetchild * returns, this driver is about 32K samples behind whereas 1952162588Snetchild * 4Front's is about 8K samples behind. Should determine source 1953162588Snetchild * of discrepancy, even if only out of curiosity. 1954162588Snetchild * 1955162588Snetchild * @todo Actually test SNDCTL_DSP_CURRENT_IPTR. 1956162588Snetchild */ 1957162588Snetchild chn = (cmd == SNDCTL_DSP_CURRENT_OPTR) ? wrch : rdch; 1958162588Snetchild if (chn == NULL) 1959162588Snetchild ret = EINVAL; 1960162588Snetchild else { 1961162588Snetchild struct snd_dbuf *bs; 1962162588Snetchild /* int tmp; */ 1963162588Snetchild 1964162588Snetchild oss_count_t *oc = (oss_count_t *)arg; 1965162588Snetchild 1966162588Snetchild CHN_LOCK(chn); 1967162588Snetchild bs = chn->bufsoft; 1968162588Snetchild#if 0 1969162588Snetchild tmp = (sndbuf_getsize(b) + chn_getptr(chn) - sndbuf_gethwptr(b)) % sndbuf_getsize(b); 1970193640Sariff oc->samples = (sndbuf_gettotal(b) + tmp) / sndbuf_getalign(b); 1971193640Sariff oc->fifo_samples = (sndbuf_getready(b) - tmp) / sndbuf_getalign(b); 1972162588Snetchild#else 1973193640Sariff oc->samples = sndbuf_gettotal(bs) / sndbuf_getalign(bs); 1974193640Sariff oc->fifo_samples = sndbuf_getready(bs) / sndbuf_getalign(bs); 1975162588Snetchild#endif 1976162588Snetchild CHN_UNLOCK(chn); 1977162588Snetchild } 1978162588Snetchild break; 1979162588Snetchild 1980162588Snetchild case SNDCTL_DSP_HALT_OUTPUT: 1981162588Snetchild case SNDCTL_DSP_HALT_INPUT: 1982162588Snetchild chn = (cmd == SNDCTL_DSP_HALT_OUTPUT) ? wrch : rdch; 1983162588Snetchild if (chn == NULL) 1984162588Snetchild ret = EINVAL; 1985162588Snetchild else { 1986162588Snetchild CHN_LOCK(chn); 1987162588Snetchild chn_abort(chn); 1988162588Snetchild CHN_UNLOCK(chn); 1989162588Snetchild } 1990162588Snetchild break; 1991162588Snetchild 1992162588Snetchild case SNDCTL_DSP_LOW_WATER: 1993162588Snetchild /* 1994162588Snetchild * Set the number of bytes required to attract attention by 1995162588Snetchild * select/poll. 1996162588Snetchild */ 1997162588Snetchild if (wrch != NULL) { 1998162588Snetchild CHN_LOCK(wrch); 1999162588Snetchild wrch->lw = (*arg_i > 1) ? *arg_i : 1; 2000162588Snetchild CHN_UNLOCK(wrch); 2001162588Snetchild } 2002162588Snetchild if (rdch != NULL) { 2003162588Snetchild CHN_LOCK(rdch); 2004162588Snetchild rdch->lw = (*arg_i > 1) ? *arg_i : 1; 2005162588Snetchild CHN_UNLOCK(rdch); 2006162588Snetchild } 2007162588Snetchild break; 2008162588Snetchild 2009162588Snetchild case SNDCTL_DSP_GETERROR: 2010162588Snetchild /* 2011162588Snetchild * OSSv4 docs: "All errors and counters will automatically be 2012162588Snetchild * cleared to zeroes after the call so each call will return only 2013162588Snetchild * the errors that occurred after the previous invocation. ... The 2014249585Sgabor * play_underruns and rec_overrun fields are the only useful fields 2015162588Snetchild * returned by OSS 4.0." 2016162588Snetchild */ 2017162588Snetchild { 2018162588Snetchild audio_errinfo *ei = (audio_errinfo *)arg; 2019162588Snetchild 2020162588Snetchild bzero((void *)ei, sizeof(*ei)); 2021162588Snetchild 2022162588Snetchild if (wrch != NULL) { 2023162588Snetchild CHN_LOCK(wrch); 2024162588Snetchild ei->play_underruns = wrch->xruns; 2025162588Snetchild wrch->xruns = 0; 2026162588Snetchild CHN_UNLOCK(wrch); 2027162588Snetchild } 2028162588Snetchild if (rdch != NULL) { 2029162588Snetchild CHN_LOCK(rdch); 2030162588Snetchild ei->rec_overruns = rdch->xruns; 2031162588Snetchild rdch->xruns = 0; 2032162588Snetchild CHN_UNLOCK(rdch); 2033162588Snetchild } 2034162588Snetchild } 2035162588Snetchild break; 2036162588Snetchild 2037162588Snetchild case SNDCTL_DSP_SYNCGROUP: 2038170815Sariff PCM_ACQUIRE_QUICK(d); 2039162588Snetchild ret = dsp_oss_syncgroup(wrch, rdch, (oss_syncgroup *)arg); 2040170815Sariff PCM_RELEASE_QUICK(d); 2041162588Snetchild break; 2042162588Snetchild 2043162588Snetchild case SNDCTL_DSP_SYNCSTART: 2044170815Sariff PCM_ACQUIRE_QUICK(d); 2045162588Snetchild ret = dsp_oss_syncstart(*arg_i); 2046170815Sariff PCM_RELEASE_QUICK(d); 2047162588Snetchild break; 2048162588Snetchild 2049162588Snetchild case SNDCTL_DSP_POLICY: 2050170815Sariff PCM_ACQUIRE_QUICK(d); 2051162588Snetchild ret = dsp_oss_policy(wrch, rdch, *arg_i); 2052170815Sariff PCM_RELEASE_QUICK(d); 2053162588Snetchild break; 2054162588Snetchild 2055193640Sariff case SNDCTL_DSP_COOKEDMODE: 2056193640Sariff PCM_ACQUIRE_QUICK(d); 2057193640Sariff if (!(dsp_get_flags(i_dev) & SD_F_BITPERFECT)) 2058193640Sariff ret = dsp_oss_cookedmode(wrch, rdch, *arg_i); 2059193640Sariff PCM_RELEASE_QUICK(d); 2060193640Sariff break; 2061193640Sariff case SNDCTL_DSP_GET_CHNORDER: 2062193640Sariff PCM_ACQUIRE_QUICK(d); 2063193640Sariff ret = dsp_oss_getchnorder(wrch, rdch, (unsigned long long *)arg); 2064193640Sariff PCM_RELEASE_QUICK(d); 2065193640Sariff break; 2066193640Sariff case SNDCTL_DSP_SET_CHNORDER: 2067193640Sariff PCM_ACQUIRE_QUICK(d); 2068193640Sariff ret = dsp_oss_setchnorder(wrch, rdch, (unsigned long long *)arg); 2069193640Sariff PCM_RELEASE_QUICK(d); 2070193640Sariff break; 2071193640Sariff case SNDCTL_DSP_GETCHANNELMASK: /* XXX vlc */ 2072193640Sariff PCM_ACQUIRE_QUICK(d); 2073193640Sariff ret = dsp_oss_getchannelmask(wrch, rdch, (int *)arg); 2074193640Sariff PCM_RELEASE_QUICK(d); 2075193640Sariff break; 2076193640Sariff case SNDCTL_DSP_BIND_CHANNEL: /* XXX what?!? */ 2077193640Sariff ret = EINVAL; 2078193640Sariff break; 2079162588Snetchild#ifdef OSSV4_EXPERIMENT 2080162588Snetchild /* 2081162588Snetchild * XXX The following ioctls are not yet supported and just return 2082162588Snetchild * EINVAL. 2083162588Snetchild */ 2084162588Snetchild case SNDCTL_DSP_GETOPEAKS: 2085162588Snetchild case SNDCTL_DSP_GETIPEAKS: 2086162588Snetchild chn = (cmd == SNDCTL_DSP_GETOPEAKS) ? wrch : rdch; 2087162588Snetchild if (chn == NULL) 2088162588Snetchild ret = EINVAL; 2089162588Snetchild else { 2090162588Snetchild oss_peaks_t *op = (oss_peaks_t *)arg; 2091162588Snetchild int lpeak, rpeak; 2092162588Snetchild 2093162588Snetchild CHN_LOCK(chn); 2094162588Snetchild ret = chn_getpeaks(chn, &lpeak, &rpeak); 2095162588Snetchild if (ret == -1) 2096162588Snetchild ret = EINVAL; 2097162588Snetchild else { 2098162588Snetchild (*op)[0] = lpeak; 2099162588Snetchild (*op)[1] = rpeak; 2100162588Snetchild } 2101162588Snetchild CHN_UNLOCK(chn); 2102162588Snetchild } 2103162588Snetchild break; 2104162588Snetchild 2105170815Sariff /* 2106170815Sariff * XXX Once implemented, revisit this for proper cv protection 2107170815Sariff * (if necessary). 2108170815Sariff */ 2109162588Snetchild case SNDCTL_GETLABEL: 2110162588Snetchild ret = dsp_oss_getlabel(wrch, rdch, (oss_label_t *)arg); 2111162588Snetchild break; 2112162588Snetchild case SNDCTL_SETLABEL: 2113162588Snetchild ret = dsp_oss_setlabel(wrch, rdch, (oss_label_t *)arg); 2114162588Snetchild break; 2115162588Snetchild case SNDCTL_GETSONG: 2116162588Snetchild ret = dsp_oss_getsong(wrch, rdch, (oss_longname_t *)arg); 2117162588Snetchild break; 2118162588Snetchild case SNDCTL_SETSONG: 2119162588Snetchild ret = dsp_oss_setsong(wrch, rdch, (oss_longname_t *)arg); 2120162588Snetchild break; 2121162588Snetchild case SNDCTL_SETNAME: 2122162588Snetchild ret = dsp_oss_setname(wrch, rdch, (oss_longname_t *)arg); 2123162588Snetchild break; 2124162588Snetchild#if 0 2125162588Snetchild /** 2126162588Snetchild * @note The S/PDIF interface ioctls, @c SNDCTL_DSP_READCTL and 2127162588Snetchild * @c SNDCTL_DSP_WRITECTL have been omitted at the suggestion of 2128162588Snetchild * 4Front Technologies. 2129162588Snetchild */ 2130162588Snetchild case SNDCTL_DSP_READCTL: 2131162588Snetchild case SNDCTL_DSP_WRITECTL: 2132162588Snetchild ret = EINVAL; 2133162588Snetchild break; 2134162588Snetchild#endif /* !0 (explicitly omitted ioctls) */ 2135162588Snetchild 2136162588Snetchild#endif /* !OSSV4_EXPERIMENT */ 213750724Scg case SNDCTL_DSP_MAPINBUF: 213850724Scg case SNDCTL_DSP_MAPOUTBUF: 213950724Scg case SNDCTL_DSP_SETSYNCRO: 214050724Scg /* undocumented */ 214150724Scg 214274763Scg case SNDCTL_DSP_SUBDIVIDE: 214350724Scg case SOUND_PCM_WRITE_FILTER: 214450724Scg case SOUND_PCM_READ_FILTER: 214550724Scg /* dunno what these do, don't sound important */ 2146131461Snetchild 214750724Scg default: 214889774Sscottl DEB(printf("default ioctl fn 0x%08lx fail\n", cmd)); 214950724Scg ret = EINVAL; 215050724Scg break; 215150724Scg } 2152170815Sariff 2153170815Sariff PCM_GIANT_LEAVE(d); 2154170815Sariff 2155170815Sariff return (ret); 215650724Scg} 215750724Scg 215878362Scgstatic int 2159130585Sphkdsp_poll(struct cdev *i_dev, int events, struct thread *td) 216050724Scg{ 2161170815Sariff struct snddev_info *d; 2162170815Sariff struct pcm_channel *wrch, *rdch; 216378214Scg int ret, e; 216450724Scg 2165170815Sariff d = dsp_get_info(i_dev); 2166170815Sariff if (!DSP_REGISTERED(d, i_dev)) 2167170815Sariff return (EBADF); 2168170815Sariff 2169170815Sariff PCM_GIANT_ENTER(d); 2170170815Sariff 2171170815Sariff wrch = NULL; 2172170815Sariff rdch = NULL; 217378214Scg ret = 0; 2174170815Sariff 217578362Scg getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); 217674763Scg 2177170815Sariff if (wrch != NULL && !(wrch->flags & CHN_F_DEAD)) { 217878214Scg e = (events & (POLLOUT | POLLWRNORM)); 217978214Scg if (e) 218083366Sjulian ret |= chn_poll(wrch, e, td); 218178214Scg } 2182170815Sariff 2183170815Sariff if (rdch != NULL && !(rdch->flags & CHN_F_DEAD)) { 218478214Scg e = (events & (POLLIN | POLLRDNORM)); 218578214Scg if (e) 218683366Sjulian ret |= chn_poll(rdch, e, td); 218778214Scg } 2188170815Sariff 218978362Scg relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); 219074763Scg 2191170815Sariff PCM_GIANT_LEAVE(d); 2192170815Sariff 2193170815Sariff return (ret); 219450724Scg} 219550724Scg 219678362Scgstatic int 2197201223Srnolanddsp_mmap(struct cdev *i_dev, vm_ooffset_t offset, vm_paddr_t *paddr, 2198201223Srnoland int nprot, vm_memattr_t *memattr) 219950724Scg{ 2200221803Savg 2201221803Savg /* XXX memattr is not honored */ 2202221803Savg *paddr = vtophys(offset); 2203221803Savg return (0); 2204221803Savg} 2205221803Savg 2206221803Savgstatic int 2207221803Savgdsp_mmap_single(struct cdev *i_dev, vm_ooffset_t *offset, 2208221803Savg vm_size_t size, struct vm_object **object, int nprot) 2209221803Savg{ 2210170815Sariff struct snddev_info *d; 2211170815Sariff struct pcm_channel *wrch, *rdch, *c; 221250724Scg 2213170815Sariff /* 2214170815Sariff * Reject PROT_EXEC by default. It just doesn't makes sense. 2215170815Sariff * Unfortunately, we have to give up this one due to linux_mmap 2216170815Sariff * changes. 2217170815Sariff * 2218170815Sariff * http://lists.freebsd.org/pipermail/freebsd-emulation/2007-June/003698.html 2219170815Sariff * 2220170815Sariff */ 2221193640Sariff#ifdef SV_ABI_LINUX 2222193640Sariff if ((nprot & PROT_EXEC) && (dsp_mmap_allow_prot_exec < 0 || 2223193640Sariff (dsp_mmap_allow_prot_exec == 0 && 2224193640Sariff SV_CURPROC_ABI() != SV_ABI_LINUX))) 2225193640Sariff#else 2226193640Sariff if ((nprot & PROT_EXEC) && dsp_mmap_allow_prot_exec < 1) 2227193640Sariff#endif 2228221803Savg return (EINVAL); 222978362Scg 2230221803Savg /* 2231221803Savg * PROT_READ (alone) selects the input buffer. 2232221803Savg * PROT_WRITE (alone) selects the output buffer. 2233221803Savg * PROT_WRITE|PROT_READ together select the output buffer. 2234221803Savg */ 2235221803Savg if ((nprot & (PROT_READ | PROT_WRITE)) == 0) 2236221803Savg return (EINVAL); 2237221803Savg 2238170815Sariff d = dsp_get_info(i_dev); 2239170815Sariff if (!DSP_REGISTERED(d, i_dev)) 2240221803Savg return (EINVAL); 2241170815Sariff 2242170815Sariff PCM_GIANT_ENTER(d); 2243170815Sariff 224478362Scg getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); 2245170815Sariff 2246221803Savg c = ((nprot & PROT_WRITE) != 0) ? wrch : rdch; 2247170815Sariff if (c == NULL || (c->flags & CHN_F_MMAP_INVALID) || 2248221803Savg (*offset + size) > sndbuf_getsize(c->bufsoft) || 2249170815Sariff (wrch != NULL && (wrch->flags & CHN_F_MMAP_INVALID)) || 2250170815Sariff (rdch != NULL && (rdch->flags & CHN_F_MMAP_INVALID))) { 225178362Scg relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); 2252170815Sariff PCM_GIANT_EXIT(d); 2253221803Savg return (EINVAL); 225478362Scg } 225578362Scg 2256170815Sariff if (wrch != NULL) 2257193640Sariff wrch->flags |= CHN_F_MMAP; 2258170815Sariff if (rdch != NULL) 2259193640Sariff rdch->flags |= CHN_F_MMAP; 226089683Scg 2261221809Savg *offset = (uintptr_t)sndbuf_getbufofs(c->bufsoft, *offset); 226278362Scg relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); 2263221803Savg *object = vm_pager_allocate(OBJT_DEVICE, i_dev, 2264221803Savg size, nprot, *offset, curthread->td_ucred); 226578362Scg 2266170815Sariff PCM_GIANT_LEAVE(d); 2267170815Sariff 2268221803Savg if (*object == NULL) 2269221803Savg return (EINVAL); 2270170815Sariff return (0); 227150724Scg} 227250724Scg 2273170161Sariff/* So much for dev_stdclone() */ 2274170161Sariffstatic int 2275170161Sariffdsp_stdclone(char *name, char *namep, char *sep, int use_sep, int *u, int *c) 2276170161Sariff{ 2277170161Sariff size_t len; 2278170161Sariff 2279170161Sariff len = strlen(namep); 2280170161Sariff 2281170161Sariff if (bcmp(name, namep, len) != 0) 2282170161Sariff return (ENODEV); 2283170161Sariff 2284170161Sariff name += len; 2285170161Sariff 2286170161Sariff if (isdigit(*name) == 0) 2287170161Sariff return (ENODEV); 2288170161Sariff 2289170161Sariff len = strlen(sep); 2290170161Sariff 2291170161Sariff if (*name == '0' && !(name[1] == '\0' || bcmp(name + 1, sep, len) == 0)) 2292170161Sariff return (ENODEV); 2293170161Sariff 2294170161Sariff for (*u = 0; isdigit(*name) != 0; name++) { 2295170161Sariff *u *= 10; 2296170161Sariff *u += *name - '0'; 2297170161Sariff if (*u > dsp_umax) 2298170161Sariff return (ENODEV); 2299170161Sariff } 2300170161Sariff 2301170161Sariff if (*name == '\0') 2302170161Sariff return ((use_sep == 0) ? 0 : ENODEV); 2303170161Sariff 2304170161Sariff if (bcmp(name, sep, len) != 0 || isdigit(name[len]) == 0) 2305170161Sariff return (ENODEV); 2306170161Sariff 2307170161Sariff name += len; 2308170161Sariff 2309170161Sariff if (*name == '0' && name[1] != '\0') 2310170161Sariff return (ENODEV); 2311170161Sariff 2312170161Sariff for (*c = 0; isdigit(*name) != 0; name++) { 2313170161Sariff *c *= 10; 2314170161Sariff *c += *name - '0'; 2315170161Sariff if (*c > dsp_cmax) 2316170161Sariff return (ENODEV); 2317170161Sariff } 2318170161Sariff 2319170161Sariff if (*name != '\0') 2320170161Sariff return (ENODEV); 2321170161Sariff 2322170161Sariff return (0); 2323170161Sariff} 2324170161Sariff 232578362Scgstatic void 2326170161Sariffdsp_clone(void *arg, 2327170161Sariff#if __FreeBSD_version >= 600034 2328170161Sariff struct ucred *cred, 2329170161Sariff#endif 2330170161Sariff char *name, int namelen, struct cdev **dev) 233178362Scg{ 2332170161Sariff struct snddev_info *d; 2333170161Sariff struct snd_clone_entry *ce; 2334170161Sariff struct pcm_channel *c; 2335170161Sariff int i, unit, udcmask, cunit, devtype, devhw, devcmax, tumax; 2336193640Sariff char *devname, *devcmp, *devsep; 233778362Scg 2338170161Sariff KASSERT(dsp_umax >= 0 && dsp_cmax >= 0, ("Uninitialized unit!")); 2339170161Sariff 2340130640Sphk if (*dev != NULL) 234178362Scg return; 234278893Scg 234378362Scg unit = -1; 2344170161Sariff cunit = -1; 2345170161Sariff devtype = -1; 2346170161Sariff devhw = 0; 2347170161Sariff devcmax = -1; 2348170161Sariff tumax = -1; 2349170161Sariff devname = NULL; 2350170161Sariff devsep = NULL; 2351170161Sariff 2352170815Sariff for (i = 0; unit == -1 && 2353170815Sariff i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])); i++) { 2354170161Sariff devtype = dsp_cdevs[i].type; 2355193640Sariff devcmp = dsp_cdevs[i].name; 2356170161Sariff devsep = dsp_cdevs[i].sep; 2357193640Sariff devname = dsp_cdevs[i].alias; 2358193640Sariff if (devname == NULL) 2359193640Sariff devname = devcmp; 2360170161Sariff devhw = dsp_cdevs[i].hw; 2361170161Sariff devcmax = dsp_cdevs[i].max - 1; 2362193640Sariff if (strcmp(name, devcmp) == 0) 236378893Scg unit = snd_unit; 2364193640Sariff else if (dsp_stdclone(name, devcmp, devsep, 2365170161Sariff dsp_cdevs[i].use_sep, &unit, &cunit) != 0) { 2366170161Sariff unit = -1; 2367170161Sariff cunit = -1; 236878893Scg } 236978893Scg } 2370170161Sariff 2371170161Sariff d = devclass_get_softc(pcm_devclass, unit); 2372170815Sariff if (!PCM_REGISTERED(d) || d->clones == NULL) 237378362Scg return; 237478362Scg 2375170815Sariff /* XXX Need Giant magic entry ??? */ 2376170815Sariff 2377193640Sariff PCM_LOCK(d); 2378170161Sariff if (snd_clone_disabled(d->clones)) { 2379193640Sariff PCM_UNLOCK(d); 2380124989Smatk return; 2381170161Sariff } 2382124989Smatk 2383170815Sariff PCM_WAIT(d); 2384170815Sariff PCM_ACQUIRE(d); 2385193640Sariff PCM_UNLOCK(d); 2386170815Sariff 2387170161Sariff udcmask = snd_u2unit(unit) | snd_d2unit(devtype); 2388124740Smatk 2389170161Sariff if (devhw != 0) { 2390170161Sariff KASSERT(devcmax <= dsp_cmax, 2391170161Sariff ("overflow: devcmax=%d, dsp_cmax=%d", devcmax, dsp_cmax)); 2392170161Sariff if (cunit > devcmax) { 2393170815Sariff PCM_RELEASE_QUICK(d); 2394124740Smatk return; 2395124740Smatk } 2396170161Sariff udcmask |= snd_c2unit(cunit); 2397170161Sariff CHN_FOREACH(c, d, channels.pcm) { 2398170161Sariff CHN_LOCK(c); 2399170161Sariff if (c->unit != udcmask) { 2400170161Sariff CHN_UNLOCK(c); 2401170161Sariff continue; 2402170161Sariff } 2403170161Sariff CHN_UNLOCK(c); 2404170161Sariff udcmask &= ~snd_c2unit(cunit); 2405170161Sariff /* 2406170161Sariff * Temporarily increase clone maxunit to overcome 2407170161Sariff * vchan flexibility. 2408170161Sariff * 2409170161Sariff * # sysctl dev.pcm.0.play.vchans=256 2410170161Sariff * dev.pcm.0.play.vchans: 1 -> 256 2411170161Sariff * # cat /dev/zero > /dev/dsp0.vp255 & 2412170161Sariff * [1] 17296 2413170161Sariff * # sysctl dev.pcm.0.play.vchans=0 2414170161Sariff * dev.pcm.0.play.vchans: 256 -> 1 2415170161Sariff * # fg 2416170161Sariff * [1] + running cat /dev/zero > /dev/dsp0.vp255 2417170161Sariff * ^C 2418170161Sariff * # cat /dev/zero > /dev/dsp0.vp255 2419170161Sariff * zsh: operation not supported: /dev/dsp0.vp255 2420170161Sariff */ 2421170161Sariff tumax = snd_clone_getmaxunit(d->clones); 2422170161Sariff if (cunit > tumax) 2423170161Sariff snd_clone_setmaxunit(d->clones, cunit); 2424170161Sariff else 2425170161Sariff tumax = -1; 2426170161Sariff goto dsp_clone_alloc; 2427170161Sariff } 2428170161Sariff /* 2429170161Sariff * Ok, so we're requesting unallocated vchan, but still 2430170161Sariff * within maximum vchan limit. 2431170161Sariff */ 2432170161Sariff if (((devtype == SND_DEV_DSPHW_VPLAY && d->pvchancount > 0) || 2433170161Sariff (devtype == SND_DEV_DSPHW_VREC && d->rvchancount > 0)) && 2434170161Sariff cunit < snd_maxautovchans) { 2435170161Sariff udcmask &= ~snd_c2unit(cunit); 2436170161Sariff tumax = snd_clone_getmaxunit(d->clones); 2437170161Sariff if (cunit > tumax) 2438170161Sariff snd_clone_setmaxunit(d->clones, cunit); 2439170161Sariff else 2440170161Sariff tumax = -1; 2441170161Sariff goto dsp_clone_alloc; 2442170161Sariff } 2443170815Sariff PCM_RELEASE_QUICK(d); 2444170161Sariff return; 244578395Scg } 2446170161Sariff 2447170161Sariffdsp_clone_alloc: 2448170161Sariff ce = snd_clone_alloc(d->clones, dev, &cunit, udcmask); 2449170161Sariff if (tumax != -1) 2450170161Sariff snd_clone_setmaxunit(d->clones, tumax); 2451170161Sariff if (ce != NULL) { 2452170161Sariff udcmask |= snd_c2unit(cunit); 2453193640Sariff *dev = make_dev(&dsp_cdevsw, PCMMINOR(udcmask), 2454170161Sariff UID_ROOT, GID_WHEEL, 0666, "%s%d%s%d", 2455170161Sariff devname, unit, devsep, cunit); 2456170161Sariff snd_clone_register(ce, *dev); 2457170161Sariff } 2458170161Sariff 2459170815Sariff PCM_RELEASE_QUICK(d); 2460170815Sariff 2461170161Sariff if (*dev != NULL) 2462170161Sariff dev_ref(*dev); 246378362Scg} 246478362Scg 246578362Scgstatic void 246678362Scgdsp_sysinit(void *p) 246778362Scg{ 2468170161Sariff if (dsp_ehtag != NULL) 2469170161Sariff return; 2470170161Sariff /* initialize unit numbering */ 2471170161Sariff snd_unit_init(); 2472170161Sariff dsp_umax = PCMMAXUNIT; 2473170161Sariff dsp_cmax = PCMMAXCHAN; 247478362Scg dsp_ehtag = EVENTHANDLER_REGISTER(dev_clone, dsp_clone, 0, 1000); 247578362Scg} 247678362Scg 247778362Scgstatic void 247878362Scgdsp_sysuninit(void *p) 247978362Scg{ 2480170161Sariff if (dsp_ehtag == NULL) 2481170161Sariff return; 2482170161Sariff EVENTHANDLER_DEREGISTER(dev_clone, dsp_ehtag); 2483170161Sariff dsp_ehtag = NULL; 248478362Scg} 248578362Scg 248678362ScgSYSINIT(dsp_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysinit, NULL); 248778362ScgSYSUNINIT(dsp_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysuninit, NULL); 248878362Scg 2489170161Sariffchar * 2490170161Sariffdsp_unit2name(char *buf, size_t len, int unit) 2491170161Sariff{ 2492170161Sariff int i, dtype; 2493170161Sariff 2494170505Smjacob KASSERT(buf != NULL && len != 0, 2495170505Smjacob ("bogus buf=%p len=%ju", buf, (uintmax_t)len)); 2496170161Sariff 2497170161Sariff dtype = snd_unit2d(unit); 2498170161Sariff 2499170161Sariff for (i = 0; i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])); i++) { 2500193640Sariff if (dtype != dsp_cdevs[i].type || dsp_cdevs[i].alias != NULL) 2501170161Sariff continue; 2502170161Sariff snprintf(buf, len, "%s%d%s%d", dsp_cdevs[i].name, 2503170161Sariff snd_unit2u(unit), dsp_cdevs[i].sep, snd_unit2c(unit)); 2504170161Sariff return (buf); 2505170161Sariff } 2506170161Sariff 2507170161Sariff return (NULL); 2508170161Sariff} 2509170161Sariff 2510162588Snetchild/** 2511162588Snetchild * @brief Handler for SNDCTL_AUDIOINFO. 2512162588Snetchild * 2513162588Snetchild * Gathers information about the audio device specified in ai->dev. If 2514162588Snetchild * ai->dev == -1, then this function gathers information about the current 2515162588Snetchild * device. If the call comes in on a non-audio device and ai->dev == -1, 2516162588Snetchild * return EINVAL. 2517162588Snetchild * 2518162588Snetchild * This routine is supposed to go practically straight to the hardware, 2519162588Snetchild * getting capabilities directly from the sound card driver, side-stepping 2520162588Snetchild * the intermediate channel interface. 2521162588Snetchild * 2522162588Snetchild * Note, however, that the usefulness of this command is significantly 2523162588Snetchild * decreased when requesting info about any device other than the one serving 2524162588Snetchild * the request. While each snddev_channel refers to a specific device node, 2525162588Snetchild * the converse is *not* true. Currently, when a sound device node is opened, 2526162588Snetchild * the sound subsystem scans for an available audio channel (or channels, if 2527162588Snetchild * opened in read+write) and then assigns them to the si_drv[12] private 2528162588Snetchild * data fields. As a result, any information returned linking a channel to 2529162588Snetchild * a specific character device isn't necessarily accurate. 2530162588Snetchild * 2531162588Snetchild * @note 2532162588Snetchild * Calling threads must not hold any snddev_info or pcm_channel locks. 2533162588Snetchild * 2534162588Snetchild * @param dev device on which the ioctl was issued 2535162588Snetchild * @param ai ioctl request data container 2536162588Snetchild * 2537162588Snetchild * @retval 0 success 2538162588Snetchild * @retval EINVAL ai->dev specifies an invalid device 2539162588Snetchild * 2540162588Snetchild * @todo Verify correctness of Doxygen tags. ;) 2541162588Snetchild */ 2542162588Snetchildint 2543162588Snetchilddsp_oss_audioinfo(struct cdev *i_dev, oss_audioinfo *ai) 2544162588Snetchild{ 2545162588Snetchild struct pcmchan_caps *caps; 2546162588Snetchild struct pcm_channel *ch; 2547162588Snetchild struct snddev_info *d; 2548162588Snetchild uint32_t fmts; 2549170815Sariff int i, nchan, *rates, minch, maxch; 2550170161Sariff char *devname, buf[CHN_NAMELEN]; 255178362Scg 2552162588Snetchild /* 2553162588Snetchild * If probing the device that received the ioctl, make sure it's a 2554162588Snetchild * DSP device. (Users may use this ioctl with /dev/mixer and 2555162588Snetchild * /dev/midi.) 2556162588Snetchild */ 2557170815Sariff if (ai->dev == -1 && i_dev->si_devsw != &dsp_cdevsw) 2558170815Sariff return (EINVAL); 2559162588Snetchild 2560162588Snetchild ch = NULL; 2561170161Sariff devname = NULL; 2562162588Snetchild nchan = 0; 2563170161Sariff bzero(buf, sizeof(buf)); 2564170161Sariff 2565162588Snetchild /* 2566162588Snetchild * Search for the requested audio device (channel). Start by 2567162588Snetchild * iterating over pcm devices. 2568162588Snetchild */ 2569170235Sariff for (i = 0; pcm_devclass != NULL && 2570170235Sariff i < devclass_get_maxunit(pcm_devclass); i++) { 2571162588Snetchild d = devclass_get_softc(pcm_devclass, i); 2572170815Sariff if (!PCM_REGISTERED(d)) 2573162588Snetchild continue; 2574162588Snetchild 2575170815Sariff /* XXX Need Giant magic entry ??? */ 2576170815Sariff 2577162588Snetchild /* See the note in function docblock */ 2578193640Sariff PCM_UNLOCKASSERT(d); 2579193640Sariff PCM_LOCK(d); 2580162588Snetchild 2581170161Sariff CHN_FOREACH(ch, d, channels.pcm) { 2582193640Sariff CHN_UNLOCKASSERT(ch); 2583162588Snetchild CHN_LOCK(ch); 2584162588Snetchild if (ai->dev == -1) { 2585170815Sariff if (DSP_REGISTERED(d, i_dev) && 2586170815Sariff (ch == PCM_RDCH(i_dev) || /* record ch */ 2587170815Sariff ch == PCM_WRCH(i_dev))) { /* playback ch */ 2588170815Sariff devname = dsp_unit2name(buf, 2589170815Sariff sizeof(buf), ch->unit); 2590162588Snetchild } 2591162588Snetchild } else if (ai->dev == nchan) { 2592170161Sariff devname = dsp_unit2name(buf, sizeof(buf), 2593170161Sariff ch->unit); 2594162588Snetchild } 2595170815Sariff if (devname != NULL) 2596170815Sariff break; 2597162588Snetchild CHN_UNLOCK(ch); 2598162588Snetchild ++nchan; 2599162588Snetchild } 2600162588Snetchild 2601170815Sariff if (devname != NULL) { 2602170815Sariff /* 2603170815Sariff * At this point, the following synchronization stuff 2604170815Sariff * has happened: 2605170815Sariff * - a specific PCM device is locked. 2606170815Sariff * - a specific audio channel has been locked, so be 2607170815Sariff * sure to unlock when exiting; 2608170815Sariff */ 2609162588Snetchild 2610170815Sariff caps = chn_getcaps(ch); 2611162588Snetchild 2612170815Sariff /* 2613170815Sariff * With all handles collected, zero out the user's 2614170815Sariff * container and begin filling in its fields. 2615170815Sariff */ 2616170815Sariff bzero((void *)ai, sizeof(oss_audioinfo)); 2617162588Snetchild 2618170815Sariff ai->dev = nchan; 2619170815Sariff strlcpy(ai->name, ch->name, sizeof(ai->name)); 2620162588Snetchild 2621170815Sariff if ((ch->flags & CHN_F_BUSY) == 0) 2622170815Sariff ai->busy = 0; 2623170815Sariff else 2624170815Sariff ai->busy = (ch->direction == PCMDIR_PLAY) ? OPEN_WRITE : OPEN_READ; 2625162588Snetchild 2626170815Sariff /** 2627170815Sariff * @note 2628170815Sariff * @c cmd - OSSv4 docs: "Only supported under Linux at 2629170815Sariff * this moment." Cop-out, I know, but I'll save 2630170815Sariff * running around in the process table for later. 2631170815Sariff * Is there a risk of leaking information? 2632170815Sariff */ 2633170815Sariff ai->pid = ch->pid; 2634162588Snetchild 2635170815Sariff /* 2636170815Sariff * These flags stolen from SNDCTL_DSP_GETCAPS handler. 2637170815Sariff * Note, however, that a single channel operates in 2638187030Smav * only one direction, so PCM_CAP_DUPLEX is out. 2639170815Sariff */ 2640170815Sariff /** 2641170815Sariff * @todo @c SNDCTL_AUDIOINFO::caps - Make drivers keep 2642170815Sariff * these in pcmchan::caps? 2643170815Sariff */ 2644187030Smav ai->caps = PCM_CAP_REALTIME | PCM_CAP_MMAP | PCM_CAP_TRIGGER | 2645187030Smav ((ch->direction == PCMDIR_PLAY) ? PCM_CAP_OUTPUT : PCM_CAP_INPUT); 2646162588Snetchild 2647170815Sariff /* 2648170815Sariff * Collect formats supported @b natively by the 2649170815Sariff * device. Also determine min/max channels. (I.e., 2650170815Sariff * mono, stereo, or both?) 2651170815Sariff * 2652170815Sariff * If any channel is stereo, maxch = 2; 2653170815Sariff * if all channels are stereo, minch = 2, too; 2654170815Sariff * if any channel is mono, minch = 1; 2655170815Sariff * and if all channels are mono, maxch = 1. 2656170815Sariff */ 2657170815Sariff minch = 0; 2658170815Sariff maxch = 0; 2659170815Sariff fmts = 0; 2660170815Sariff for (i = 0; caps->fmtlist[i]; i++) { 2661170815Sariff fmts |= caps->fmtlist[i]; 2662193640Sariff if (AFMT_CHANNEL(caps->fmtlist[i]) > 1) { 2663170815Sariff minch = (minch == 0) ? 2 : minch; 2664170815Sariff maxch = 2; 2665170815Sariff } else { 2666170815Sariff minch = 1; 2667170815Sariff maxch = (maxch == 0) ? 1 : maxch; 2668170815Sariff } 2669170815Sariff } 2670162588Snetchild 2671170815Sariff if (ch->direction == PCMDIR_PLAY) 2672170815Sariff ai->oformats = fmts; 2673170815Sariff else 2674170815Sariff ai->iformats = fmts; 2675170161Sariff 2676170815Sariff /** 2677170815Sariff * @note 2678170815Sariff * @c magic - OSSv4 docs: "Reserved for internal use 2679170815Sariff * by OSS." 2680170815Sariff * 2681170815Sariff * @par 2682170815Sariff * @c card_number - OSSv4 docs: "Number of the sound 2683170815Sariff * card where this device belongs or -1 if this 2684170815Sariff * information is not available. Applications 2685170815Sariff * should normally not use this field for any 2686170815Sariff * purpose." 2687170815Sariff */ 2688170815Sariff ai->card_number = -1; 2689170815Sariff /** 2690170815Sariff * @todo @c song_name - depends first on 2691170815Sariff * SNDCTL_[GS]ETSONG @todo @c label - depends 2692170815Sariff * on SNDCTL_[GS]ETLABEL 2693170815Sariff * @todo @c port_number - routing information? 2694170815Sariff */ 2695170815Sariff ai->port_number = -1; 2696170815Sariff ai->mixer_dev = (d->mixer_dev != NULL) ? PCMUNIT(d->mixer_dev) : -1; 2697170815Sariff /** 2698170815Sariff * @note 2699170815Sariff * @c real_device - OSSv4 docs: "Obsolete." 2700170815Sariff */ 2701170815Sariff ai->real_device = -1; 2702187034Smav strlcpy(ai->devnode, "/dev/", sizeof(ai->devnode)); 2703187034Smav strlcat(ai->devnode, devname, sizeof(ai->devnode)); 2704170815Sariff ai->enabled = device_is_attached(d->dev) ? 1 : 0; 2705170815Sariff /** 2706170815Sariff * @note 2707170815Sariff * @c flags - OSSv4 docs: "Reserved for future use." 2708170815Sariff * 2709170815Sariff * @note 2710170815Sariff * @c binding - OSSv4 docs: "Reserved for future use." 2711170815Sariff * 2712170815Sariff * @todo @c handle - haven't decided how to generate 2713170815Sariff * this yet; bus, vendor, device IDs? 2714170815Sariff */ 2715170815Sariff ai->min_rate = caps->minspeed; 2716170815Sariff ai->max_rate = caps->maxspeed; 2717162588Snetchild 2718170815Sariff ai->min_channels = minch; 2719170815Sariff ai->max_channels = maxch; 2720162588Snetchild 2721170815Sariff ai->nrates = chn_getrates(ch, &rates); 2722170815Sariff if (ai->nrates > OSS_MAX_SAMPLE_RATES) 2723170815Sariff ai->nrates = OSS_MAX_SAMPLE_RATES; 2724162588Snetchild 2725170815Sariff for (i = 0; i < ai->nrates; i++) 2726170815Sariff ai->rates[i] = rates[i]; 2727187030Smav 2728187030Smav ai->next_play_engine = 0; 2729187030Smav ai->next_rec_engine = 0; 2730162588Snetchild 2731170815Sariff CHN_UNLOCK(ch); 2732170815Sariff } 2733162588Snetchild 2734193640Sariff PCM_UNLOCK(d); 2735162588Snetchild 2736170815Sariff if (devname != NULL) 2737170815Sariff return (0); 2738170815Sariff } 2739162588Snetchild 2740170815Sariff /* Exhausted the search -- nothing is locked, so return. */ 2741170815Sariff return (EINVAL); 2742162588Snetchild} 2743162588Snetchild 2744162588Snetchild/** 2745162588Snetchild * @brief Assigns a PCM channel to a sync group. 2746162588Snetchild * 2747162588Snetchild * Sync groups are used to enable audio operations on multiple devices 2748162588Snetchild * simultaneously. They may be used with any number of devices and may 2749162588Snetchild * span across applications. Devices are added to groups with 2750162588Snetchild * the SNDCTL_DSP_SYNCGROUP ioctl, and operations are triggered with the 2751162588Snetchild * SNDCTL_DSP_SYNCSTART ioctl. 2752162588Snetchild * 2753162588Snetchild * If the @c id field of the @c group parameter is set to zero, then a new 2754162588Snetchild * sync group is created. Otherwise, wrch and rdch (if set) are added to 2755162588Snetchild * the group specified. 2756162588Snetchild * 2757162588Snetchild * @todo As far as memory allocation, should we assume that things are 2758162588Snetchild * okay and allocate with M_WAITOK before acquiring channel locks, 2759162588Snetchild * freeing later if not? 2760162588Snetchild * 2761162588Snetchild * @param wrch output channel associated w/ device (if any) 2762162588Snetchild * @param rdch input channel associated w/ device (if any) 2763162588Snetchild * @param group Sync group parameters 2764162588Snetchild * 2765162588Snetchild * @retval 0 success 2766162588Snetchild * @retval non-zero error to be propagated upstream 2767162588Snetchild */ 2768162588Snetchildstatic int 2769162588Snetchilddsp_oss_syncgroup(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_syncgroup *group) 2770162588Snetchild{ 2771162588Snetchild struct pcmchan_syncmember *smrd, *smwr; 2772162588Snetchild struct pcmchan_syncgroup *sg; 2773162588Snetchild int ret, sg_ids[3]; 2774162588Snetchild 2775162588Snetchild smrd = NULL; 2776162588Snetchild smwr = NULL; 2777162588Snetchild sg = NULL; 2778162588Snetchild ret = 0; 2779162588Snetchild 2780162588Snetchild /* 2781162588Snetchild * Free_unr() may sleep, so store released syncgroup IDs until after 2782162588Snetchild * all locks are released. 2783162588Snetchild */ 2784162588Snetchild sg_ids[0] = sg_ids[1] = sg_ids[2] = 0; 2785162588Snetchild 2786162588Snetchild PCM_SG_LOCK(); 2787162588Snetchild 2788162588Snetchild /* 2789162588Snetchild * - Insert channel(s) into group's member list. 2790162588Snetchild * - Set CHN_F_NOTRIGGER on channel(s). 2791162588Snetchild * - Stop channel(s). 2792162588Snetchild */ 2793162588Snetchild 2794162588Snetchild /* 2795162588Snetchild * If device's channels are already mapped to a group, unmap them. 2796162588Snetchild */ 2797162588Snetchild if (wrch) { 2798162588Snetchild CHN_LOCK(wrch); 2799162588Snetchild sg_ids[0] = chn_syncdestroy(wrch); 2800162588Snetchild } 2801162588Snetchild 2802162588Snetchild if (rdch) { 2803162588Snetchild CHN_LOCK(rdch); 2804162588Snetchild sg_ids[1] = chn_syncdestroy(rdch); 2805162588Snetchild } 2806162588Snetchild 2807162588Snetchild /* 2808162588Snetchild * Verify that mode matches character device properites. 2809162588Snetchild * - Bail if PCM_ENABLE_OUTPUT && wrch == NULL. 2810162588Snetchild * - Bail if PCM_ENABLE_INPUT && rdch == NULL. 2811162588Snetchild */ 2812162588Snetchild if (((wrch == NULL) && (group->mode & PCM_ENABLE_OUTPUT)) || 2813162588Snetchild ((rdch == NULL) && (group->mode & PCM_ENABLE_INPUT))) { 2814162588Snetchild ret = EINVAL; 2815162588Snetchild goto out; 2816162588Snetchild } 2817162588Snetchild 2818162588Snetchild /* 2819162588Snetchild * An id of zero indicates the user wants to create a new 2820162588Snetchild * syncgroup. 2821162588Snetchild */ 2822162588Snetchild if (group->id == 0) { 2823162588Snetchild sg = (struct pcmchan_syncgroup *)malloc(sizeof(*sg), M_DEVBUF, M_NOWAIT); 2824162588Snetchild if (sg != NULL) { 2825162588Snetchild SLIST_INIT(&sg->members); 2826162588Snetchild sg->id = alloc_unr(pcmsg_unrhdr); 2827162588Snetchild 2828162588Snetchild group->id = sg->id; 2829162588Snetchild SLIST_INSERT_HEAD(&snd_pcm_syncgroups, sg, link); 2830162588Snetchild } else 2831162588Snetchild ret = ENOMEM; 2832162588Snetchild } else { 2833162588Snetchild SLIST_FOREACH(sg, &snd_pcm_syncgroups, link) { 2834162588Snetchild if (sg->id == group->id) 2835162588Snetchild break; 2836162588Snetchild } 2837162588Snetchild if (sg == NULL) 2838162588Snetchild ret = EINVAL; 2839162588Snetchild } 2840162588Snetchild 2841162588Snetchild /* Couldn't create or find a syncgroup. Fail. */ 2842162588Snetchild if (sg == NULL) 2843162588Snetchild goto out; 2844162588Snetchild 2845162588Snetchild /* 2846162588Snetchild * Allocate a syncmember, assign it and a channel together, and 2847162588Snetchild * insert into syncgroup. 2848162588Snetchild */ 2849162588Snetchild if (group->mode & PCM_ENABLE_INPUT) { 2850162588Snetchild smrd = (struct pcmchan_syncmember *)malloc(sizeof(*smrd), M_DEVBUF, M_NOWAIT); 2851162588Snetchild if (smrd == NULL) { 2852162588Snetchild ret = ENOMEM; 2853162588Snetchild goto out; 2854162588Snetchild } 2855162588Snetchild 2856162588Snetchild SLIST_INSERT_HEAD(&sg->members, smrd, link); 2857162588Snetchild smrd->parent = sg; 2858162588Snetchild smrd->ch = rdch; 2859162588Snetchild 2860162588Snetchild chn_abort(rdch); 2861162588Snetchild rdch->flags |= CHN_F_NOTRIGGER; 2862162588Snetchild rdch->sm = smrd; 2863162588Snetchild } 2864162588Snetchild 2865162588Snetchild if (group->mode & PCM_ENABLE_OUTPUT) { 2866162588Snetchild smwr = (struct pcmchan_syncmember *)malloc(sizeof(*smwr), M_DEVBUF, M_NOWAIT); 2867162588Snetchild if (smwr == NULL) { 2868162588Snetchild ret = ENOMEM; 2869162588Snetchild goto out; 2870162588Snetchild } 2871162588Snetchild 2872162588Snetchild SLIST_INSERT_HEAD(&sg->members, smwr, link); 2873162588Snetchild smwr->parent = sg; 2874162588Snetchild smwr->ch = wrch; 2875162588Snetchild 2876162588Snetchild chn_abort(wrch); 2877162588Snetchild wrch->flags |= CHN_F_NOTRIGGER; 2878162588Snetchild wrch->sm = smwr; 2879162588Snetchild } 2880162588Snetchild 2881162588Snetchild 2882162588Snetchildout: 2883162588Snetchild if (ret != 0) { 2884162588Snetchild if (smrd != NULL) 2885162588Snetchild free(smrd, M_DEVBUF); 2886162588Snetchild if ((sg != NULL) && SLIST_EMPTY(&sg->members)) { 2887162588Snetchild sg_ids[2] = sg->id; 2888162588Snetchild SLIST_REMOVE(&snd_pcm_syncgroups, sg, pcmchan_syncgroup, link); 2889162588Snetchild free(sg, M_DEVBUF); 2890162588Snetchild } 2891162588Snetchild 2892162588Snetchild if (wrch) 2893162588Snetchild wrch->sm = NULL; 2894162588Snetchild if (rdch) 2895162588Snetchild rdch->sm = NULL; 2896162588Snetchild } 2897162588Snetchild 2898162588Snetchild if (wrch) 2899162588Snetchild CHN_UNLOCK(wrch); 2900162588Snetchild if (rdch) 2901162588Snetchild CHN_UNLOCK(rdch); 2902162588Snetchild 2903162588Snetchild PCM_SG_UNLOCK(); 2904162588Snetchild 2905162588Snetchild if (sg_ids[0]) 2906162588Snetchild free_unr(pcmsg_unrhdr, sg_ids[0]); 2907162588Snetchild if (sg_ids[1]) 2908162588Snetchild free_unr(pcmsg_unrhdr, sg_ids[1]); 2909162588Snetchild if (sg_ids[2]) 2910162588Snetchild free_unr(pcmsg_unrhdr, sg_ids[2]); 2911162588Snetchild 2912170815Sariff return (ret); 2913162588Snetchild} 2914162588Snetchild 2915162588Snetchild/** 2916162588Snetchild * @brief Launch a sync group into action 2917162588Snetchild * 2918162588Snetchild * Sync groups are established via SNDCTL_DSP_SYNCGROUP. This function 2919162588Snetchild * iterates over all members, triggering them along the way. 2920162588Snetchild * 2921162588Snetchild * @note Caller must not hold any channel locks. 2922162588Snetchild * 2923162588Snetchild * @param sg_id sync group identifier 2924162588Snetchild * 2925162588Snetchild * @retval 0 success 2926162588Snetchild * @retval non-zero error worthy of propagating upstream to user 2927162588Snetchild */ 2928162588Snetchildstatic int 2929162588Snetchilddsp_oss_syncstart(int sg_id) 2930162588Snetchild{ 2931162588Snetchild struct pcmchan_syncmember *sm, *sm_tmp; 2932162588Snetchild struct pcmchan_syncgroup *sg; 2933162588Snetchild struct pcm_channel *c; 2934162588Snetchild int ret, needlocks; 2935170161Sariff 2936162588Snetchild /* Get the synclists lock */ 2937162588Snetchild PCM_SG_LOCK(); 2938162588Snetchild 2939162588Snetchild do { 2940162588Snetchild ret = 0; 2941162588Snetchild needlocks = 0; 2942162588Snetchild 2943162588Snetchild /* Search for syncgroup by ID */ 2944162588Snetchild SLIST_FOREACH(sg, &snd_pcm_syncgroups, link) { 2945162588Snetchild if (sg->id == sg_id) 2946162588Snetchild break; 2947162588Snetchild } 2948162588Snetchild 2949162588Snetchild /* Return EINVAL if not found */ 2950162588Snetchild if (sg == NULL) { 2951162588Snetchild ret = EINVAL; 2952162588Snetchild break; 2953162588Snetchild } 2954162588Snetchild 2955162588Snetchild /* Any removals resulting in an empty group should've handled this */ 2956162588Snetchild KASSERT(!SLIST_EMPTY(&sg->members), ("found empty syncgroup")); 2957162588Snetchild 2958162588Snetchild /* 2959162588Snetchild * Attempt to lock all member channels - if any are already 2960162588Snetchild * locked, unlock those acquired, sleep for a bit, and try 2961162588Snetchild * again. 2962162588Snetchild */ 2963162588Snetchild SLIST_FOREACH(sm, &sg->members, link) { 2964162588Snetchild if (CHN_TRYLOCK(sm->ch) == 0) { 2965162588Snetchild int timo = hz * 5/1000; 2966162588Snetchild if (timo < 1) 2967162588Snetchild timo = 1; 2968162588Snetchild 2969162588Snetchild /* Release all locked channels so far, retry */ 2970162588Snetchild SLIST_FOREACH(sm_tmp, &sg->members, link) { 2971162588Snetchild /* sm is the member already locked */ 2972162588Snetchild if (sm == sm_tmp) 2973162588Snetchild break; 2974162588Snetchild CHN_UNLOCK(sm_tmp->ch); 2975162588Snetchild } 2976162588Snetchild 2977162588Snetchild /** @todo Is PRIBIO correct/ */ 2978170815Sariff ret = msleep(sm, &snd_pcm_syncgroups_mtx, 2979170815Sariff PRIBIO | PCATCH, "pcmsg", timo); 2980162588Snetchild if (ret == EINTR || ret == ERESTART) 2981162588Snetchild break; 2982162588Snetchild 2983162588Snetchild needlocks = 1; 2984167647Sariff ret = 0; /* Assumes ret == EAGAIN... */ 2985162588Snetchild } 2986162588Snetchild } 2987162588Snetchild } while (needlocks && ret == 0); 2988162588Snetchild 2989162588Snetchild /* Proceed only if no errors encountered. */ 2990162588Snetchild if (ret == 0) { 2991162588Snetchild /* Launch channels */ 2992193640Sariff while ((sm = SLIST_FIRST(&sg->members)) != NULL) { 2993162588Snetchild SLIST_REMOVE_HEAD(&sg->members, link); 2994162588Snetchild 2995162588Snetchild c = sm->ch; 2996162588Snetchild c->sm = NULL; 2997162588Snetchild chn_start(c, 1); 2998162588Snetchild c->flags &= ~CHN_F_NOTRIGGER; 2999162588Snetchild CHN_UNLOCK(c); 3000162588Snetchild 3001162588Snetchild free(sm, M_DEVBUF); 3002162588Snetchild } 3003162588Snetchild 3004162588Snetchild SLIST_REMOVE(&snd_pcm_syncgroups, sg, pcmchan_syncgroup, link); 3005162588Snetchild free(sg, M_DEVBUF); 3006162588Snetchild } 3007162588Snetchild 3008162588Snetchild PCM_SG_UNLOCK(); 3009162588Snetchild 3010162588Snetchild /* 3011162588Snetchild * Free_unr() may sleep, so be sure to give up the syncgroup lock 3012162588Snetchild * first. 3013162588Snetchild */ 3014162588Snetchild if (ret == 0) 3015162588Snetchild free_unr(pcmsg_unrhdr, sg_id); 3016162588Snetchild 3017170815Sariff return (ret); 3018162588Snetchild} 3019162588Snetchild 3020162588Snetchild/** 3021162588Snetchild * @brief Handler for SNDCTL_DSP_POLICY 3022162588Snetchild * 3023162588Snetchild * The SNDCTL_DSP_POLICY ioctl is a simpler interface to control fragment 3024162588Snetchild * size and count like with SNDCTL_DSP_SETFRAGMENT. Instead of the user 3025162588Snetchild * specifying those two parameters, s/he simply selects a number from 0..10 3026162588Snetchild * which corresponds to a buffer size. Smaller numbers request smaller 3027162588Snetchild * buffers with lower latencies (at greater overhead from more frequent 3028162588Snetchild * interrupts), while greater numbers behave in the opposite manner. 3029162588Snetchild * 3030162588Snetchild * The 4Front spec states that a value of 5 should be the default. However, 3031162588Snetchild * this implementation deviates slightly by using a linear scale without 3032162588Snetchild * consulting drivers. I.e., even though drivers may have different default 3033162588Snetchild * buffer sizes, a policy argument of 5 will have the same result across 3034162588Snetchild * all drivers. 3035162588Snetchild * 3036162588Snetchild * See http://manuals.opensound.com/developer/SNDCTL_DSP_POLICY.html for 3037162588Snetchild * more information. 3038162588Snetchild * 3039162588Snetchild * @todo When SNDCTL_DSP_COOKEDMODE is supported, it'll be necessary to 3040162588Snetchild * work with hardware drivers directly. 3041162588Snetchild * 3042162588Snetchild * @note PCM channel arguments must not be locked by caller. 3043162588Snetchild * 3044162588Snetchild * @param wrch Pointer to opened playback channel (optional; may be NULL) 3045162588Snetchild * @param rdch " recording channel (optional; may be NULL) 3046162588Snetchild * @param policy Integer from [0:10] 3047162588Snetchild * 3048162588Snetchild * @retval 0 constant (for now) 3049162588Snetchild */ 3050162588Snetchildstatic int 3051162588Snetchilddsp_oss_policy(struct pcm_channel *wrch, struct pcm_channel *rdch, int policy) 3052162588Snetchild{ 3053164614Sariff int ret; 3054162588Snetchild 3055164614Sariff if (policy < CHN_POLICY_MIN || policy > CHN_POLICY_MAX) 3056170815Sariff return (EIO); 3057164614Sariff 3058162588Snetchild /* Default: success */ 3059162588Snetchild ret = 0; 3060162588Snetchild 3061162588Snetchild if (rdch) { 3062162588Snetchild CHN_LOCK(rdch); 3063164614Sariff ret = chn_setlatency(rdch, policy); 3064162588Snetchild CHN_UNLOCK(rdch); 3065162588Snetchild } 3066162588Snetchild 3067162588Snetchild if (wrch && ret == 0) { 3068162588Snetchild CHN_LOCK(wrch); 3069164614Sariff ret = chn_setlatency(wrch, policy); 3070162588Snetchild CHN_UNLOCK(wrch); 3071162588Snetchild } 3072162588Snetchild 3073164614Sariff if (ret) 3074164614Sariff ret = EIO; 3075164614Sariff 3076170815Sariff return (ret); 3077162588Snetchild} 3078162588Snetchild 3079162588Snetchild/** 3080162588Snetchild * @brief Enable or disable "cooked" mode 3081162588Snetchild * 3082162588Snetchild * This is a handler for @c SNDCTL_DSP_COOKEDMODE. When in cooked mode, which 3083162588Snetchild * is the default, the sound system handles rate and format conversions 3084162588Snetchild * automatically (ex: user writing 11025Hz/8 bit/unsigned but card only 3085162588Snetchild * operates with 44100Hz/16bit/signed samples). 3086162588Snetchild * 3087162588Snetchild * Disabling cooked mode is intended for applications wanting to mmap() 3088162588Snetchild * a sound card's buffer space directly, bypassing the FreeBSD 2-stage 3089162588Snetchild * feeder architecture, presumably to gain as much control over audio 3090162588Snetchild * hardware as possible. 3091162588Snetchild * 3092162588Snetchild * See @c http://manuals.opensound.com/developer/SNDCTL_DSP_COOKEDMODE.html 3093162588Snetchild * for more details. 3094162588Snetchild * 3095162588Snetchild * @param wrch playback channel (optional; may be NULL) 3096162588Snetchild * @param rdch recording channel (optional; may be NULL) 3097162588Snetchild * @param enabled 0 = raw mode, 1 = cooked mode 3098162588Snetchild * 3099162588Snetchild * @retval EINVAL Operation not yet supported. 3100162588Snetchild */ 3101162588Snetchildstatic int 3102162588Snetchilddsp_oss_cookedmode(struct pcm_channel *wrch, struct pcm_channel *rdch, int enabled) 3103162588Snetchild{ 3104193640Sariff 3105193640Sariff /* 3106193640Sariff * XXX I just don't get it. Why don't they call it 3107193640Sariff * "BITPERFECT" ~ SNDCTL_DSP_BITPERFECT !?!?. 3108193640Sariff * This is just plain so confusing, incoherent, 3109193640Sariff * <insert any non-printable characters here>. 3110193640Sariff */ 3111193640Sariff if (!(enabled == 1 || enabled == 0)) 3112193640Sariff return (EINVAL); 3113193640Sariff 3114193640Sariff /* 3115193640Sariff * I won't give in. I'm inverting its logic here and now. 3116193640Sariff * Brag all you want, but "BITPERFECT" should be the better 3117193640Sariff * term here. 3118193640Sariff */ 3119193640Sariff enabled ^= 0x00000001; 3120193640Sariff 3121193640Sariff if (wrch != NULL) { 3122193640Sariff CHN_LOCK(wrch); 3123193640Sariff wrch->flags &= ~CHN_F_BITPERFECT; 3124193640Sariff wrch->flags |= (enabled != 0) ? CHN_F_BITPERFECT : 0x00000000; 3125193640Sariff CHN_UNLOCK(wrch); 3126193640Sariff } 3127193640Sariff 3128193640Sariff if (rdch != NULL) { 3129193640Sariff CHN_LOCK(rdch); 3130193640Sariff rdch->flags &= ~CHN_F_BITPERFECT; 3131193640Sariff rdch->flags |= (enabled != 0) ? CHN_F_BITPERFECT : 0x00000000; 3132193640Sariff CHN_UNLOCK(rdch); 3133193640Sariff } 3134193640Sariff 3135193640Sariff return (0); 3136162588Snetchild} 3137162588Snetchild 3138162588Snetchild/** 3139162588Snetchild * @brief Retrieve channel interleaving order 3140162588Snetchild * 3141162588Snetchild * This is the handler for @c SNDCTL_DSP_GET_CHNORDER. 3142162588Snetchild * 3143162588Snetchild * See @c http://manuals.opensound.com/developer/SNDCTL_DSP_GET_CHNORDER.html 3144162588Snetchild * for more details. 3145162588Snetchild * 3146162588Snetchild * @note As the ioctl definition is still under construction, FreeBSD 3147162588Snetchild * does not currently support SNDCTL_DSP_GET_CHNORDER. 3148162588Snetchild * 3149162588Snetchild * @param wrch playback channel (optional; may be NULL) 3150162588Snetchild * @param rdch recording channel (optional; may be NULL) 3151162588Snetchild * @param map channel map (result will be stored there) 3152162588Snetchild * 3153162588Snetchild * @retval EINVAL Operation not yet supported. 3154162588Snetchild */ 3155162588Snetchildstatic int 3156162588Snetchilddsp_oss_getchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map) 3157162588Snetchild{ 3158193640Sariff struct pcm_channel *ch; 3159193640Sariff int ret; 3160193640Sariff 3161193640Sariff ch = (wrch != NULL) ? wrch : rdch; 3162193640Sariff if (ch != NULL) { 3163193640Sariff CHN_LOCK(ch); 3164193640Sariff ret = chn_oss_getorder(ch, map); 3165193640Sariff CHN_UNLOCK(ch); 3166193640Sariff } else 3167193640Sariff ret = EINVAL; 3168193640Sariff 3169193640Sariff return (ret); 3170162588Snetchild} 3171162588Snetchild 3172162588Snetchild/** 3173162588Snetchild * @brief Specify channel interleaving order 3174162588Snetchild * 3175162588Snetchild * This is the handler for @c SNDCTL_DSP_SET_CHNORDER. 3176162588Snetchild * 3177162588Snetchild * @note As the ioctl definition is still under construction, FreeBSD 3178162588Snetchild * does not currently support @c SNDCTL_DSP_SET_CHNORDER. 3179162588Snetchild * 3180162588Snetchild * @param wrch playback channel (optional; may be NULL) 3181162588Snetchild * @param rdch recording channel (optional; may be NULL) 3182162588Snetchild * @param map channel map 3183162588Snetchild * 3184162588Snetchild * @retval EINVAL Operation not yet supported. 3185162588Snetchild */ 3186162588Snetchildstatic int 3187162588Snetchilddsp_oss_setchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map) 3188162588Snetchild{ 3189193640Sariff int ret; 3190193640Sariff 3191193640Sariff ret = 0; 3192193640Sariff 3193193640Sariff if (wrch != NULL) { 3194193640Sariff CHN_LOCK(wrch); 3195193640Sariff ret = chn_oss_setorder(wrch, map); 3196193640Sariff CHN_UNLOCK(wrch); 3197193640Sariff } 3198193640Sariff 3199193640Sariff if (ret == 0 && rdch != NULL) { 3200193640Sariff CHN_LOCK(rdch); 3201193640Sariff ret = chn_oss_setorder(rdch, map); 3202193640Sariff CHN_UNLOCK(rdch); 3203193640Sariff } 3204193640Sariff 3205193640Sariff return (ret); 3206162588Snetchild} 3207162588Snetchild 3208193640Sariffstatic int 3209193640Sariffdsp_oss_getchannelmask(struct pcm_channel *wrch, struct pcm_channel *rdch, 3210193640Sariff int *mask) 3211193640Sariff{ 3212193640Sariff struct pcm_channel *ch; 3213193640Sariff uint32_t chnmask; 3214193640Sariff int ret; 3215193640Sariff 3216193640Sariff chnmask = 0; 3217193640Sariff ch = (wrch != NULL) ? wrch : rdch; 3218193640Sariff 3219193640Sariff if (ch != NULL) { 3220193640Sariff CHN_LOCK(ch); 3221193640Sariff ret = chn_oss_getmask(ch, &chnmask); 3222193640Sariff CHN_UNLOCK(ch); 3223193640Sariff } else 3224193640Sariff ret = EINVAL; 3225193640Sariff 3226193640Sariff if (ret == 0) 3227193640Sariff *mask = chnmask; 3228193640Sariff 3229193640Sariff return (ret); 3230193640Sariff} 3231193640Sariff 3232193640Sariff#ifdef OSSV4_EXPERIMENT 3233162588Snetchild/** 3234162588Snetchild * @brief Retrieve an audio device's label 3235162588Snetchild * 3236162588Snetchild * This is a handler for the @c SNDCTL_GETLABEL ioctl. 3237162588Snetchild * 3238162588Snetchild * See @c http://manuals.opensound.com/developer/SNDCTL_GETLABEL.html 3239162588Snetchild * for more details. 3240162588Snetchild * 3241162588Snetchild * From Hannu@4Front: "For example ossxmix (just like some HW mixer 3242162588Snetchild * consoles) can show variable "labels" for certain controls. By default 3243162588Snetchild * the application name (say quake) is shown as the label but 3244162588Snetchild * applications may change the labels themselves." 3245162588Snetchild * 3246162588Snetchild * @note As the ioctl definition is still under construction, FreeBSD 3247162588Snetchild * does not currently support @c SNDCTL_GETLABEL. 3248162588Snetchild * 3249162588Snetchild * @param wrch playback channel (optional; may be NULL) 3250162588Snetchild * @param rdch recording channel (optional; may be NULL) 3251162588Snetchild * @param label label gets copied here 3252162588Snetchild * 3253162588Snetchild * @retval EINVAL Operation not yet supported. 3254162588Snetchild */ 3255162588Snetchildstatic int 3256162588Snetchilddsp_oss_getlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label) 3257162588Snetchild{ 3258170815Sariff return (EINVAL); 3259162588Snetchild} 3260162588Snetchild 3261162588Snetchild/** 3262162588Snetchild * @brief Specify an audio device's label 3263162588Snetchild * 3264162588Snetchild * This is a handler for the @c SNDCTL_SETLABEL ioctl. Please see the 3265162588Snetchild * comments for @c dsp_oss_getlabel immediately above. 3266162588Snetchild * 3267162588Snetchild * See @c http://manuals.opensound.com/developer/SNDCTL_GETLABEL.html 3268162588Snetchild * for more details. 3269162588Snetchild * 3270162588Snetchild * @note As the ioctl definition is still under construction, FreeBSD 3271162588Snetchild * does not currently support SNDCTL_SETLABEL. 3272162588Snetchild * 3273162588Snetchild * @param wrch playback channel (optional; may be NULL) 3274162588Snetchild * @param rdch recording channel (optional; may be NULL) 3275162588Snetchild * @param label label gets copied from here 3276162588Snetchild * 3277162588Snetchild * @retval EINVAL Operation not yet supported. 3278162588Snetchild */ 3279162588Snetchildstatic int 3280162588Snetchilddsp_oss_setlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label) 3281162588Snetchild{ 3282170815Sariff return (EINVAL); 3283162588Snetchild} 3284162588Snetchild 3285162588Snetchild/** 3286162588Snetchild * @brief Retrieve name of currently played song 3287162588Snetchild * 3288162588Snetchild * This is a handler for the @c SNDCTL_GETSONG ioctl. Audio players could 3289162588Snetchild * tell the system the name of the currently playing song, which would be 3290162588Snetchild * visible in @c /dev/sndstat. 3291162588Snetchild * 3292162588Snetchild * See @c http://manuals.opensound.com/developer/SNDCTL_GETSONG.html 3293162588Snetchild * for more details. 3294162588Snetchild * 3295162588Snetchild * @note As the ioctl definition is still under construction, FreeBSD 3296162588Snetchild * does not currently support SNDCTL_GETSONG. 3297162588Snetchild * 3298162588Snetchild * @param wrch playback channel (optional; may be NULL) 3299162588Snetchild * @param rdch recording channel (optional; may be NULL) 3300162588Snetchild * @param song song name gets copied here 3301162588Snetchild * 3302162588Snetchild * @retval EINVAL Operation not yet supported. 3303162588Snetchild */ 3304162588Snetchildstatic int 3305162588Snetchilddsp_oss_getsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song) 3306162588Snetchild{ 3307170815Sariff return (EINVAL); 3308162588Snetchild} 3309162588Snetchild 3310162588Snetchild/** 3311162588Snetchild * @brief Retrieve name of currently played song 3312162588Snetchild * 3313162588Snetchild * This is a handler for the @c SNDCTL_SETSONG ioctl. Audio players could 3314162588Snetchild * tell the system the name of the currently playing song, which would be 3315162588Snetchild * visible in @c /dev/sndstat. 3316162588Snetchild * 3317162588Snetchild * See @c http://manuals.opensound.com/developer/SNDCTL_SETSONG.html 3318162588Snetchild * for more details. 3319162588Snetchild * 3320162588Snetchild * @note As the ioctl definition is still under construction, FreeBSD 3321162588Snetchild * does not currently support SNDCTL_SETSONG. 3322162588Snetchild * 3323162588Snetchild * @param wrch playback channel (optional; may be NULL) 3324162588Snetchild * @param rdch recording channel (optional; may be NULL) 3325162588Snetchild * @param song song name gets copied from here 3326162588Snetchild * 3327162588Snetchild * @retval EINVAL Operation not yet supported. 3328162588Snetchild */ 3329162588Snetchildstatic int 3330162588Snetchilddsp_oss_setsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song) 3331162588Snetchild{ 3332170815Sariff return (EINVAL); 3333162588Snetchild} 3334162588Snetchild 3335162588Snetchild/** 3336162588Snetchild * @brief Rename a device 3337162588Snetchild * 3338162588Snetchild * This is a handler for the @c SNDCTL_SETNAME ioctl. 3339162588Snetchild * 3340162588Snetchild * See @c http://manuals.opensound.com/developer/SNDCTL_SETNAME.html for 3341162588Snetchild * more details. 3342162588Snetchild * 3343162588Snetchild * From Hannu@4Front: "This call is used to change the device name 3344162588Snetchild * reported in /dev/sndstat and ossinfo. So instead of using some generic 3345162588Snetchild * 'OSS loopback audio (MIDI) driver' the device may be given a meaningfull 3346162588Snetchild * name depending on the current context (for example 'OSS virtual wave table 3347162588Snetchild * synth' or 'VoIP link to London')." 3348162588Snetchild * 3349162588Snetchild * @note As the ioctl definition is still under construction, FreeBSD 3350162588Snetchild * does not currently support SNDCTL_SETNAME. 3351162588Snetchild * 3352162588Snetchild * @param wrch playback channel (optional; may be NULL) 3353162588Snetchild * @param rdch recording channel (optional; may be NULL) 3354162588Snetchild * @param name new device name gets copied from here 3355162588Snetchild * 3356162588Snetchild * @retval EINVAL Operation not yet supported. 3357162588Snetchild */ 3358162588Snetchildstatic int 3359162588Snetchilddsp_oss_setname(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *name) 3360162588Snetchild{ 3361170815Sariff return (EINVAL); 3362162588Snetchild} 3363162588Snetchild#endif /* !OSSV4_EXPERIMENT */ 3364