dsp.c revision 102172
1139749Simp/* 2122526Ssimokawa * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> 3122526Ssimokawa * All rights reserved. 4103285Sikob * 5103285Sikob * Redistribution and use in source and binary forms, with or without 6103285Sikob * modification, are permitted provided that the following conditions 7103285Sikob * are met: 8103285Sikob * 1. Redistributions of source code must retain the above copyright 9103285Sikob * notice, this list of conditions and the following disclaimer. 10103285Sikob * 2. Redistributions in binary form must reproduce the above copyright 11103285Sikob * notice, this list of conditions and the following disclaimer in the 12103285Sikob * documentation and/or other materials provided with the distribution. 13103285Sikob * 14103285Sikob * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15103285Sikob * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16103285Sikob * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17103285Sikob * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18103285Sikob * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19103285Sikob * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20103285Sikob * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21103285Sikob * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22103285Sikob * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23103285Sikob * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24103285Sikob * SUCH DAMAGE. 25103285Sikob */ 26103285Sikob 27103285Sikob#include <sys/param.h> 28103285Sikob#include <sys/queue.h> 29103285Sikob 30103285Sikob#include <dev/sound/pcm/sound.h> 31103285Sikob 32103285SikobSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/dsp.c 102172 2002-08-20 08:02:56Z orion $"); 33103285Sikob 34103285Sikob#define OLDPCM_IOCTL 35103285Sikob 36103285Sikobstatic d_open_t dsp_open; 37103285Sikobstatic d_close_t dsp_close; 38103285Sikobstatic d_read_t dsp_read; 39103285Sikobstatic d_write_t dsp_write; 40103285Sikobstatic d_ioctl_t dsp_ioctl; 41103285Sikobstatic d_poll_t dsp_poll; 42127468Ssimokawastatic d_mmap_t dsp_mmap; 43103285Sikob 44103285Sikobstatic struct cdevsw dsp_cdevsw = { 45103285Sikob /* open */ dsp_open, 46127468Ssimokawa /* close */ dsp_close, 47117126Sscottl /* read */ dsp_read, 48117126Sscottl /* write */ dsp_write, 49117732Ssimokawa /* ioctl */ dsp_ioctl, 50103285Sikob /* poll */ dsp_poll, 51127468Ssimokawa /* mmap */ dsp_mmap, 52112136Ssimokawa /* strategy */ nostrategy, 53112136Ssimokawa /* name */ "dsp", 54103285Sikob /* maj */ SND_CDEV_MAJOR, 55127468Ssimokawa /* dump */ nodump, 56127468Ssimokawa /* psize */ nopsize, 57127468Ssimokawa /* flags */ 0, 58127468Ssimokawa}; 59127468Ssimokawa 60127468Ssimokawa#ifdef USING_DEVFS 61127468Ssimokawastatic eventhandler_tag dsp_ehtag; 62127468Ssimokawa#endif 63127468Ssimokawa 64127468Ssimokawastatic struct snddev_info * 65127468Ssimokawadsp_get_info(dev_t dev) 66127468Ssimokawa{ 67127468Ssimokawa struct snddev_info *d; 68127468Ssimokawa int unit; 69127468Ssimokawa 70103285Sikob unit = PCMUNIT(dev); 71103285Sikob if (unit >= devclass_get_maxunit(pcm_devclass)) 72103285Sikob return NULL; 73103285Sikob d = devclass_get_softc(pcm_devclass, unit); 74103285Sikob 75103285Sikob return d; 76103285Sikob} 77103285Sikob 78103285Sikobstatic u_int32_t 79103285Sikobdsp_get_flags(dev_t dev) 80113584Ssimokawa{ 81103285Sikob device_t bdev; 82120660Ssimokawa int unit; 83127468Ssimokawa 84103285Sikob unit = PCMUNIT(dev); 85103285Sikob if (unit >= devclass_get_maxunit(pcm_devclass)) 86103285Sikob return 0xffffffff; 87103285Sikob bdev = devclass_get_device(pcm_devclass, unit); 88111615Ssimokawa 89121185Ssimokawa return pcm_getflags(bdev); 90121185Ssimokawa} 91121185Ssimokawa 92121185Ssimokawastatic void 93121185Ssimokawadsp_set_flags(dev_t dev, u_int32_t flags) 94167622Ssimokawa{ 95113584Ssimokawa device_t bdev; 96113584Ssimokawa int unit; 97113584Ssimokawa 98103285Sikob unit = PCMUNIT(dev); 99113584Ssimokawa if (unit >= devclass_get_maxunit(pcm_devclass)) 100111615Ssimokawa return; 101111615Ssimokawa bdev = devclass_get_device(pcm_devclass, unit); 102111615Ssimokawa 103111615Ssimokawa pcm_setflags(bdev, flags); 104130532Sdfr} 105111615Ssimokawa 106111615Ssimokawa/* 107120660Ssimokawa * return the channels channels associated with an open device instance. 108111615Ssimokawa * set the priority if the device is simplex and one direction (only) is 109111615Ssimokawa * specified. 110111615Ssimokawa * lock channels specified. 111103285Sikob */ 112120660Ssimokawastatic int 113120660Ssimokawagetchns(dev_t dev, struct pcm_channel **rdch, struct pcm_channel **wrch, u_int32_t prio) 114120660Ssimokawa{ 115120660Ssimokawa struct snddev_info *d; 116103285Sikob u_int32_t flags; 117103285Sikob 118120660Ssimokawa flags = dsp_get_flags(dev); 119103285Sikob d = dsp_get_info(dev); 120103285Sikob pcm_lock(d); 121120660Ssimokawa pcm_inprog(d, 1); 122103285Sikob KASSERT((flags & SD_F_PRIO_SET) != SD_F_PRIO_SET, \ 123103285Sikob ("getchns: read and write both prioritised")); 124111203Ssimokawa 125103285Sikob if ((flags & SD_F_PRIO_SET) == 0 && (prio != (SD_F_PRIO_RD | SD_F_PRIO_WR))) { 126124251Ssimokawa flags |= prio & (SD_F_PRIO_RD | SD_F_PRIO_WR); 127111199Ssimokawa dsp_set_flags(dev, flags); 128122387Ssimokawa } 129122387Ssimokawa 130122387Ssimokawa *rdch = dev->si_drv1; 131127468Ssimokawa *wrch = dev->si_drv2; 132127468Ssimokawa if ((flags & SD_F_SIMPLEX) && (flags & SD_F_PRIO_SET)) { 133103285Sikob if (prio) { 134103285Sikob if (*rdch && flags & SD_F_PRIO_WR) { 135227309Sed dev->si_drv1 = NULL; 136227309Sed *rdch = pcm_getfakechan(d); 137103285Sikob } else if (*wrch && flags & SD_F_PRIO_RD) { 138103285Sikob dev->si_drv2 = NULL; 139103285Sikob *wrch = pcm_getfakechan(d); 140103285Sikob } 141103285Sikob } 142103285Sikob 143121792Ssimokawa pcm_getfakechan(d)->flags |= CHN_F_BUSY; 144130677Ssimokawa } 145122387Ssimokawa pcm_unlock(d); 146122387Ssimokawa 147122387Ssimokawa if (*rdch && *rdch != pcm_getfakechan(d) && (prio & SD_F_PRIO_RD)) 148122387Ssimokawa CHN_LOCK(*rdch); 149127468Ssimokawa if (*wrch && *wrch != pcm_getfakechan(d) && (prio & SD_F_PRIO_WR)) 150127468Ssimokawa CHN_LOCK(*wrch); 151127468Ssimokawa 152127468Ssimokawa return 0; 153103285Sikob} 154122387Ssimokawa 155122387Ssimokawa/* unlock specified channels */ 156122387Ssimokawastatic void 157122387Ssimokawarelchns(dev_t dev, struct pcm_channel *rdch, struct pcm_channel *wrch, u_int32_t prio) 158122387Ssimokawa{ 159127468Ssimokawa struct snddev_info *d; 160127468Ssimokawa 161122387Ssimokawa d = dsp_get_info(dev); 162103285Sikob if (wrch && wrch != pcm_getfakechan(d) && (prio & SD_F_PRIO_WR)) 163103285Sikob CHN_UNLOCK(wrch); 164113584Ssimokawa if (rdch && rdch != pcm_getfakechan(d) && (prio & SD_F_PRIO_RD)) 165113584Ssimokawa CHN_UNLOCK(rdch); 166167622Ssimokawa pcm_lock(d); 167113584Ssimokawa pcm_inprog(d, -1); 168167622Ssimokawa pcm_unlock(d); 169113584Ssimokawa} 170103285Sikob 171103285Sikobstatic int 172103285Sikobdsp_open(dev_t i_dev, int flags, int mode, struct thread *td) 173113584Ssimokawa{ 174129585Sdfr struct pcm_channel *rdch, *wrch; 175129585Sdfr struct snddev_info *d; 176120660Ssimokawa intrmask_t s; 177103285Sikob u_int32_t fmt; 178113584Ssimokawa int devtype; 179103285Sikob 180255871Sscottl s = spltty(); 181103285Sikob d = dsp_get_info(i_dev); 182113584Ssimokawa devtype = PCMDEV(i_dev); 183103285Sikob 184103285Sikob /* decide default format */ 185113584Ssimokawa switch (devtype) { 186103285Sikob case SND_DEV_DSP16: 187103285Sikob fmt = AFMT_S16_LE; 188103285Sikob break; 189114732Ssimokawa 190110336Ssimokawa case SND_DEV_DSP: 191103285Sikob fmt = AFMT_U8; 192110336Ssimokawa break; 193103285Sikob 194103285Sikob case SND_DEV_AUDIO: 195103285Sikob fmt = AFMT_MU_LAW; 196103285Sikob break; 197103285Sikob 198129585Sdfr case SND_DEV_NORESET: 199114732Ssimokawa fmt = 0; 200129585Sdfr break; 201129585Sdfr 202129585Sdfr case SND_DEV_DSPREC: 203121185Ssimokawa fmt = AFMT_U8; 204121185Ssimokawa if (mode & FWRITE) { 205121185Ssimokawa splx(s); 206121185Ssimokawa return EINVAL; 207127468Ssimokawa } 208127468Ssimokawa break; 209127468Ssimokawa 210129585Sdfr default: 211103285Sikob panic("impossible devtype %d", devtype); 212103285Sikob } 213113584Ssimokawa 214113584Ssimokawa /* lock snddev so nobody else can monkey with it */ 215111615Ssimokawa pcm_lock(d); 216113584Ssimokawa if ((dsp_get_flags(i_dev) & SD_F_SIMPLEX) && (i_dev->si_drv1 || i_dev->si_drv2)) { 217103285Sikob /* simplex device, already open, exit */ 218113584Ssimokawa pcm_unlock(d); 219127468Ssimokawa splx(s); 220103285Sikob return EBUSY; 221103285Sikob } 222103285Sikob 223188756Ssbruno /* if we get here, the open request is valid */ 224103285Sikob rdch = i_dev->si_drv1; 225103285Sikob wrch = i_dev->si_drv2; 226103285Sikob 227103285Sikob if (flags & FREAD) { 228103285Sikob /* open for read */ 229121185Ssimokawa if (rdch == NULL) { 230103285Sikob /* not already open, try to get a channel */ 231103285Sikob if (devtype == SND_DEV_DSPREC) 232129585Sdfr rdch = pcm_chnalloc(d, PCMDIR_REC, td->td_proc->p_pid, PCMCHAN(i_dev)); 233111615Ssimokawa else 234111615Ssimokawa rdch = pcm_chnalloc(d, PCMDIR_REC, td->td_proc->p_pid, -1); 235111615Ssimokawa if (!rdch) { 236111615Ssimokawa /* no channel available, exit */ 237113584Ssimokawa pcm_unlock(d); 238113584Ssimokawa splx(s); 239103285Sikob return EBUSY; 240103285Sikob } 241103285Sikob /* got a channel, already locked for us */ 242103285Sikob } else { 243103285Sikob /* already open for read, exit */ 244111615Ssimokawa pcm_unlock(d); 245103285Sikob splx(s); 246103285Sikob return EBUSY; 247103285Sikob } 248122387Ssimokawa } 249124145Ssimokawa 250124145Ssimokawa if (flags & FWRITE) { 251170374Ssimokawa /* open for write */ 252103285Sikob if (wrch == NULL) { 253170374Ssimokawa /* not already open, try to get a channel */ 254170374Ssimokawa wrch = pcm_chnalloc(d, PCMDIR_PLAY, td->td_proc->p_pid, -1); 255122387Ssimokawa if (!wrch) { 256124169Ssimokawa /* no channel available */ 257124169Ssimokawa if (rdch && (flags & FREAD)) { 258124169Ssimokawa /* just opened a read channel, release it */ 259121185Ssimokawa pcm_chnrelease(rdch); 260124169Ssimokawa } 261121185Ssimokawa /* exit */ 262124169Ssimokawa pcm_unlock(d); 263127468Ssimokawa splx(s); 264124169Ssimokawa return EBUSY; 265124169Ssimokawa } 266124169Ssimokawa /* got a channel, already locked for us */ 267124169Ssimokawa } else { 268170807Ssimokawa /* already open for write */ 269124169Ssimokawa if (rdch && (flags & FREAD)) { 270124169Ssimokawa /* just opened a read channel, release it */ 271124169Ssimokawa pcm_chnrelease(rdch); 272124169Ssimokawa } 273121185Ssimokawa /* exit */ 274121185Ssimokawa pcm_unlock(d); 275124169Ssimokawa splx(s); 276124169Ssimokawa return EBUSY; 277124169Ssimokawa } 278124169Ssimokawa } 279124169Ssimokawa 280103285Sikob i_dev->si_drv1 = rdch; 281227293Sed i_dev->si_drv2 = wrch; 282103285Sikob pcm_unlock(d); 283103285Sikob /* finished with snddev, new channels still locked */ 284103285Sikob 285103285Sikob /* bump refcounts, reset and unlock any channels that we just opened */ 286111615Ssimokawa if (rdch) { 287111615Ssimokawa if (flags & FREAD) { 288103285Sikob if (chn_reset(rdch, fmt)) { 289103285Sikob pcm_lock(d); 290103285Sikob pcm_chnrelease(rdch); 291103285Sikob if (wrch && (flags & FWRITE)) 292103285Sikob pcm_chnrelease(wrch); 293103285Sikob pcm_unlock(d); 294103285Sikob splx(s); 295103285Sikob return ENODEV; 296103285Sikob } 297103285Sikob if (flags & O_NONBLOCK) 298103285Sikob rdch->flags |= CHN_F_NBIO; 299103285Sikob } else 300103285Sikob CHN_LOCK(rdch); 301103285Sikob 302103285Sikob pcm_chnref(rdch, 1); 303103285Sikob CHN_UNLOCK(rdch); 304103285Sikob } 305103285Sikob if (wrch) { 306103285Sikob if (flags & FWRITE) { 307103285Sikob if (chn_reset(wrch, fmt)) { 308103285Sikob pcm_lock(d); 309103285Sikob pcm_chnrelease(wrch); 310103285Sikob if (rdch && (flags & FREAD)) 311103285Sikob pcm_chnrelease(rdch); 312103285Sikob pcm_unlock(d); 313103285Sikob splx(s); 314103285Sikob return ENODEV; 315103285Sikob } 316103285Sikob if (flags & O_NONBLOCK) 317103285Sikob wrch->flags |= CHN_F_NBIO; 318103285Sikob } else 319103285Sikob CHN_LOCK(wrch); 320103285Sikob 321103285Sikob pcm_chnref(wrch, 1); 322103285Sikob CHN_UNLOCK(wrch); 323103285Sikob } 324103285Sikob splx(s); 325103285Sikob return 0; 326103285Sikob} 327103285Sikob 328103285Sikobstatic int 329103285Sikobdsp_close(dev_t i_dev, int flags, int mode, struct thread *td) 330103285Sikob{ 331103285Sikob struct pcm_channel *rdch, *wrch; 332103285Sikob struct snddev_info *d; 333103285Sikob intrmask_t s; 334103285Sikob int exit; 335103285Sikob 336103285Sikob s = spltty(); 337103285Sikob d = dsp_get_info(i_dev); 338103285Sikob pcm_lock(d); 339103285Sikob rdch = i_dev->si_drv1; 340188393Sfjoe wrch = i_dev->si_drv2; 341103285Sikob 342103285Sikob exit = 0; 343103285Sikob 344103285Sikob /* decrement refcount for each channel, exit if nonzero */ 345103285Sikob if (rdch) { 346103285Sikob CHN_LOCK(rdch); 347103285Sikob if (pcm_chnref(rdch, -1) > 0) { 348103285Sikob CHN_UNLOCK(rdch); 349103285Sikob exit = 1; 350103285Sikob } 351103285Sikob } 352103285Sikob if (wrch) { 353103285Sikob CHN_LOCK(wrch); 354103285Sikob if (pcm_chnref(wrch, -1) > 0) { 355103285Sikob CHN_UNLOCK(wrch); 356103285Sikob exit = 1; 357103285Sikob } 358103285Sikob } 359103285Sikob if (exit) { 360120660Ssimokawa pcm_unlock(d); 361111615Ssimokawa splx(s); 362132432Ssimokawa return 0; 363111615Ssimokawa } 364111615Ssimokawa 365132432Ssimokawa /* both refcounts are zero, abort and release */ 366132432Ssimokawa 367103285Sikob if (pcm_getfakechan(d)) 368103285Sikob pcm_getfakechan(d)->flags = 0; 369103285Sikob 370188756Ssbruno i_dev->si_drv1 = NULL; 371188756Ssbruno i_dev->si_drv2 = NULL; 372188756Ssbruno 373103285Sikob dsp_set_flags(i_dev, dsp_get_flags(i_dev) & ~SD_F_TRANSIENT); 374188756Ssbruno pcm_unlock(d); 375103285Sikob 376103285Sikob if (rdch) { 377103285Sikob chn_abort(rdch); /* won't sleep */ 378103285Sikob rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD); 379188756Ssbruno chn_reset(rdch, 0); 380188756Ssbruno pcm_chnrelease(rdch); 381188756Ssbruno } 382188756Ssbruno if (wrch) { 383188756Ssbruno chn_flush(wrch); /* may sleep */ 384108503Ssimokawa wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD); 385108503Ssimokawa chn_reset(wrch, 0); 386103285Sikob pcm_chnrelease(wrch); 387103285Sikob } 388103285Sikob 389103285Sikob splx(s); 390188756Ssbruno return 0; 391188756Ssbruno} 392188756Ssbruno 393188756Ssbrunostatic int 394188756Ssbrunodsp_read(dev_t i_dev, struct uio *buf, int flag) 395188756Ssbruno{ 396188756Ssbruno struct pcm_channel *rdch, *wrch; 397188756Ssbruno intrmask_t s; 398188756Ssbruno int ret; 399103285Sikob 400103285Sikob s = spltty(); 401110184Ssimokawa getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD); 402110184Ssimokawa 403110184Ssimokawa KASSERT(rdch, ("dsp_read: nonexistant channel")); 404110184Ssimokawa KASSERT(rdch->flags & CHN_F_BUSY, ("dsp_read: nonbusy channel")); 405110184Ssimokawa 406110184Ssimokawa if (rdch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) { 407110184Ssimokawa relchns(i_dev, rdch, wrch, SD_F_PRIO_RD); 408110184Ssimokawa splx(s); 409110184Ssimokawa return EINVAL; 410110184Ssimokawa } 411110184Ssimokawa if (!(rdch->flags & CHN_F_RUNNING)) 412110184Ssimokawa rdch->flags |= CHN_F_RUNNING; 413110184Ssimokawa ret = chn_read(rdch, buf); 414110184Ssimokawa relchns(i_dev, rdch, wrch, SD_F_PRIO_RD); 415110184Ssimokawa 416110184Ssimokawa splx(s); 417110184Ssimokawa return ret; 418110184Ssimokawa} 419110184Ssimokawa 420110184Ssimokawastatic int 421110184Ssimokawadsp_write(dev_t i_dev, struct uio *buf, int flag) 422110184Ssimokawa{ 423110184Ssimokawa struct pcm_channel *rdch, *wrch; 424110184Ssimokawa intrmask_t s; 425110184Ssimokawa int ret; 426110184Ssimokawa 427110184Ssimokawa s = spltty(); 428110184Ssimokawa getchns(i_dev, &rdch, &wrch, SD_F_PRIO_WR); 429110184Ssimokawa 430110184Ssimokawa KASSERT(wrch, ("dsp_write: nonexistant channel")); 431110184Ssimokawa KASSERT(wrch->flags & CHN_F_BUSY, ("dsp_write: nonbusy channel")); 432110184Ssimokawa 433110184Ssimokawa if (wrch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) { 434110184Ssimokawa relchns(i_dev, rdch, wrch, SD_F_PRIO_WR); 435110184Ssimokawa splx(s); 436110184Ssimokawa return EINVAL; 437110184Ssimokawa } 438110184Ssimokawa if (!(wrch->flags & CHN_F_RUNNING)) 439110184Ssimokawa wrch->flags |= CHN_F_RUNNING; 440110184Ssimokawa ret = chn_write(wrch, buf); 441110184Ssimokawa relchns(i_dev, rdch, wrch, SD_F_PRIO_WR); 442110184Ssimokawa 443110184Ssimokawa splx(s); 444110184Ssimokawa return ret; 445110184Ssimokawa} 446110184Ssimokawa 447110184Ssimokawastatic int 448110184Ssimokawadsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td) 449110184Ssimokawa{ 450110184Ssimokawa struct pcm_channel *wrch, *rdch; 451110184Ssimokawa struct snddev_info *d; 452110184Ssimokawa intrmask_t s; 453121185Ssimokawa int kill; 454121185Ssimokawa int ret = 0, *arg_i = (int *)arg, tmp; 455103285Sikob 456108503Ssimokawa /* 457108503Ssimokawa * this is an evil hack to allow broken apps to perform mixer ioctls 458121185Ssimokawa * on dsp devices. 459121185Ssimokawa */ 460121185Ssimokawa 461103285Sikob if (IOCGROUP(cmd) == 'M') { 462121185Ssimokawa dev_t pdev; 463114069Ssimokawa 464108503Ssimokawa pdev = makedev(SND_CDEV_MAJOR, PCMMKMINOR(PCMUNIT(i_dev), SND_DEV_CTL, 0)); 465108503Ssimokawa return mixer_ioctl(pdev, cmd, arg, mode, td); 466108503Ssimokawa } 467108503Ssimokawa 468108503Ssimokawa s = spltty(); 469108503Ssimokawa d = dsp_get_info(i_dev); 470110839Ssimokawa getchns(i_dev, &rdch, &wrch, 0); 471108642Ssimokawa 472108642Ssimokawa kill = 0; 473108642Ssimokawa if (wrch && (wrch->flags & CHN_F_DEAD)) 474108503Ssimokawa kill |= 1; 475108503Ssimokawa if (rdch && (rdch->flags & CHN_F_DEAD)) 476108503Ssimokawa kill |= 2; 477108503Ssimokawa if (kill == 3) { 478110839Ssimokawa relchns(i_dev, rdch, wrch, 0); 479121185Ssimokawa splx(s); 480121185Ssimokawa return EINVAL; 481121185Ssimokawa } 482121185Ssimokawa if (kill & 1) 483121185Ssimokawa wrch = NULL; 484110839Ssimokawa if (kill & 2) 485110839Ssimokawa rdch = NULL; 486121185Ssimokawa 487121185Ssimokawa switch(cmd) { 488121185Ssimokawa#ifdef OLDPCM_IOCTL 489121185Ssimokawa /* 490121185Ssimokawa * we start with the new ioctl interface. 491121185Ssimokawa */ 492121185Ssimokawa case AIONWRITE: /* how many bytes can write ? */ 493121185Ssimokawa/* 494121185Ssimokawa if (wrch && wrch->bufhard.dl) 495121185Ssimokawa while (chn_wrfeed(wrch) == 0); 496121185Ssimokawa*/ 497187993Ssbruno *arg_i = wrch? sndbuf_getfree(wrch->bufsoft) : 0; 498121185Ssimokawa break; 499103285Sikob 500121185Ssimokawa case AIOSSIZE: /* set the current blocksize */ 501121185Ssimokawa { 502121185Ssimokawa struct snd_size *p = (struct snd_size *)arg; 503121185Ssimokawa 504121185Ssimokawa p->play_size = 0; 505121185Ssimokawa p->rec_size = 0; 506121185Ssimokawa if (wrch) { 507121185Ssimokawa CHN_LOCK(wrch); 508127468Ssimokawa chn_setblocksize(wrch, 2, p->play_size); 509121185Ssimokawa p->play_size = sndbuf_getblksz(wrch->bufsoft); 510121185Ssimokawa CHN_UNLOCK(wrch); 511121185Ssimokawa } 512121185Ssimokawa if (rdch) { 513121185Ssimokawa CHN_LOCK(rdch); 514121185Ssimokawa chn_setblocksize(rdch, 2, p->rec_size); 515121185Ssimokawa p->rec_size = sndbuf_getblksz(rdch->bufsoft); 516121185Ssimokawa CHN_UNLOCK(rdch); 517121185Ssimokawa } 518121185Ssimokawa } 519121185Ssimokawa break; 520121185Ssimokawa case AIOGSIZE: /* get the current blocksize */ 521121185Ssimokawa { 522121185Ssimokawa struct snd_size *p = (struct snd_size *)arg; 523121185Ssimokawa 524121185Ssimokawa if (wrch) 525121185Ssimokawa p->play_size = sndbuf_getblksz(wrch->bufsoft); 526108503Ssimokawa if (rdch) 527108503Ssimokawa p->rec_size = sndbuf_getblksz(rdch->bufsoft); 528121185Ssimokawa } 529121185Ssimokawa break; 530108503Ssimokawa 531108503Ssimokawa case AIOSFMT: 532108503Ssimokawa { 533110839Ssimokawa snd_chan_param *p = (snd_chan_param *)arg; 534110839Ssimokawa 535110839Ssimokawa if (wrch) { 536121185Ssimokawa CHN_LOCK(wrch); 537110839Ssimokawa chn_setformat(wrch, p->play_format); 538121185Ssimokawa chn_setspeed(wrch, p->play_rate); 539121185Ssimokawa CHN_UNLOCK(wrch); 540121185Ssimokawa } 541121185Ssimokawa if (rdch) { 542121185Ssimokawa CHN_LOCK(rdch); 543121185Ssimokawa chn_setformat(rdch, p->rec_format); 544127468Ssimokawa chn_setspeed(rdch, p->rec_rate); 545121185Ssimokawa CHN_UNLOCK(rdch); 546121185Ssimokawa } 547121185Ssimokawa } 548121185Ssimokawa /* FALLTHROUGH */ 549121185Ssimokawa 550121185Ssimokawa case AIOGFMT: 551121185Ssimokawa { 552121185Ssimokawa snd_chan_param *p = (snd_chan_param *)arg; 553121185Ssimokawa 554188756Ssbruno p->play_rate = wrch? wrch->speed : 0; 555188756Ssbruno p->rec_rate = rdch? rdch->speed : 0; 556188756Ssbruno p->play_format = wrch? wrch->format : 0; 557188756Ssbruno p->rec_format = rdch? rdch->format : 0; 558121185Ssimokawa } 559121185Ssimokawa break; 560119196Ssimokawa 561113584Ssimokawa case AIOGCAP: /* get capabilities */ 562121185Ssimokawa { 563121185Ssimokawa snd_capabilities *p = (snd_capabilities *)arg; 564121185Ssimokawa struct pcmchan_caps *pcaps = NULL, *rcaps = NULL; 565113584Ssimokawa dev_t pdev; 566129585Sdfr 567219543Smarius if (rdch) { 568219543Smarius CHN_LOCK(rdch); 569113584Ssimokawa rcaps = chn_getcaps(rdch); 570113584Ssimokawa } 571127468Ssimokawa if (wrch) { 572121185Ssimokawa CHN_LOCK(wrch); 573121185Ssimokawa pcaps = chn_getcaps(wrch); 574121185Ssimokawa } 575113584Ssimokawa p->rate_min = max(rcaps? rcaps->minspeed : 0, 576113584Ssimokawa pcaps? pcaps->minspeed : 0); 577113584Ssimokawa p->rate_max = min(rcaps? rcaps->maxspeed : 1000000, 578113584Ssimokawa pcaps? pcaps->maxspeed : 1000000); 579113584Ssimokawa p->bufsize = min(rdch? sndbuf_getsize(rdch->bufsoft) : 1000000, 580113584Ssimokawa wrch? sndbuf_getsize(wrch->bufsoft) : 1000000); 581113584Ssimokawa /* XXX bad on sb16 */ 582113584Ssimokawa p->formats = (rdch? chn_getformats(rdch) : 0xffffffff) & 583113584Ssimokawa (wrch? chn_getformats(wrch) : 0xffffffff); 584113584Ssimokawa if (rdch && wrch) 585113584Ssimokawa p->formats |= (dsp_get_flags(i_dev) & SD_F_SIMPLEX)? 0 : AFMT_FULLDUPLEX; 586113584Ssimokawa pdev = makedev(SND_CDEV_MAJOR, PCMMKMINOR(PCMUNIT(i_dev), SND_DEV_CTL, 0)); 587113584Ssimokawa p->mixers = 1; /* default: one mixer */ 588113584Ssimokawa p->inputs = pdev->si_drv1? mix_getdevs(pdev->si_drv1) : 0; 589113584Ssimokawa p->left = p->right = 100; 590113584Ssimokawa if (wrch) 591113584Ssimokawa CHN_UNLOCK(wrch); 592121185Ssimokawa if (rdch) 593121185Ssimokawa CHN_UNLOCK(rdch); 594113584Ssimokawa } 595255871Sscottl break; 596113584Ssimokawa 597113584Ssimokawa case AIOSTOP: 598121185Ssimokawa if (*arg_i == AIOSYNC_PLAY && wrch) 599108503Ssimokawa *arg_i = chn_abort(wrch); 600103285Sikob else if (*arg_i == AIOSYNC_CAPTURE && rdch) 601121185Ssimokawa *arg_i = chn_abort(rdch); 602121185Ssimokawa else { 603121185Ssimokawa printf("AIOSTOP: bad channel 0x%x\n", *arg_i); 604121185Ssimokawa *arg_i = 0; 605121185Ssimokawa } 606121185Ssimokawa break; 607121185Ssimokawa 608121185Ssimokawa case AIOSYNC: 609121185Ssimokawa printf("AIOSYNC chan 0x%03lx pos %lu unimplemented\n", 610121185Ssimokawa ((snd_sync_parm *)arg)->chan, ((snd_sync_parm *)arg)->pos); 611121185Ssimokawa break; 612121185Ssimokawa#endif 613121185Ssimokawa /* 614121185Ssimokawa * here follow the standard ioctls (filio.h etc.) 615121185Ssimokawa */ 616121185Ssimokawa case FIONREAD: /* get # bytes to read */ 617121185Ssimokawa/* if (rdch && rdch->bufhard.dl) 618121185Ssimokawa while (chn_rdfeed(rdch) == 0); 619121185Ssimokawa*/ *arg_i = rdch? sndbuf_getready(rdch->bufsoft) : 0; 620121185Ssimokawa break; 621121185Ssimokawa 622121185Ssimokawa case FIOASYNC: /*set/clear async i/o */ 623121185Ssimokawa DEB( printf("FIOASYNC\n") ; ) 624121185Ssimokawa break; 625121185Ssimokawa 626121185Ssimokawa case SNDCTL_DSP_NONBLOCK: 627121185Ssimokawa case FIONBIO: /* set/clear non-blocking i/o */ 628121185Ssimokawa if (rdch) 629121185Ssimokawa rdch->flags &= ~CHN_F_NBIO; 630121185Ssimokawa if (wrch) 631121185Ssimokawa wrch->flags &= ~CHN_F_NBIO; 632121185Ssimokawa if (*arg_i) { 633121185Ssimokawa if (rdch) 634121185Ssimokawa rdch->flags |= CHN_F_NBIO; 635121185Ssimokawa if (wrch) 636121185Ssimokawa wrch->flags |= CHN_F_NBIO; 637121185Ssimokawa } 638121185Ssimokawa break; 639121185Ssimokawa 640121185Ssimokawa /* 641121185Ssimokawa * Finally, here is the linux-compatible ioctl interface 642121185Ssimokawa */ 643121185Ssimokawa#define THE_REAL_SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int) 644121185Ssimokawa case THE_REAL_SNDCTL_DSP_GETBLKSIZE: 645121185Ssimokawa case SNDCTL_DSP_GETBLKSIZE: 646121185Ssimokawa if (wrch) 647121185Ssimokawa *arg_i = sndbuf_getblksz(wrch->bufsoft); 648121185Ssimokawa else if (rdch) 649121185Ssimokawa *arg_i = sndbuf_getblksz(rdch->bufsoft); 650121185Ssimokawa else 651121185Ssimokawa *arg_i = 0; 652121185Ssimokawa break ; 653121185Ssimokawa 654121185Ssimokawa case SNDCTL_DSP_SETBLKSIZE: 655121185Ssimokawa RANGE(*arg_i, 16, 65536); 656114069Ssimokawa if (wrch) { 657103285Sikob CHN_LOCK(wrch); 658103285Sikob chn_setblocksize(wrch, 2, *arg_i); 659103285Sikob CHN_UNLOCK(wrch); 660103285Sikob } 661103285Sikob if (rdch) { 662103285Sikob CHN_LOCK(rdch); 663114069Ssimokawa chn_setblocksize(rdch, 2, *arg_i); 664114069Ssimokawa CHN_UNLOCK(rdch); 665103285Sikob } 666103285Sikob break; 667103285Sikob 668114069Ssimokawa case SNDCTL_DSP_RESET: 669114069Ssimokawa DEB(printf("dsp reset\n")); 670114069Ssimokawa if (wrch) 671114069Ssimokawa chn_abort(wrch); 672114069Ssimokawa if (rdch) 673114069Ssimokawa chn_abort(rdch); 674114069Ssimokawa break; 675114223Ssimokawa 676114260Ssimokawa case SNDCTL_DSP_SYNC: 677114223Ssimokawa DEB(printf("dsp sync\n")); 678114223Ssimokawa /* chn_sync may sleep */ 679114260Ssimokawa if (wrch) { 680114260Ssimokawa CHN_LOCK(wrch); 681114069Ssimokawa chn_sync(wrch, sndbuf_getsize(wrch->bufsoft) - 4); 682114069Ssimokawa CHN_UNLOCK(wrch); 683114069Ssimokawa } 684114069Ssimokawa break; 685114069Ssimokawa 686114069Ssimokawa case SNDCTL_DSP_SPEED: 687114069Ssimokawa /* chn_setspeed may sleep */ 688114069Ssimokawa tmp = 0; 689114069Ssimokawa if (wrch) { 690103285Sikob CHN_LOCK(wrch); 691111615Ssimokawa ret = chn_setspeed(wrch, *arg_i); 692103285Sikob tmp = wrch->speed; 693111615Ssimokawa CHN_UNLOCK(wrch); 694103285Sikob } 695111615Ssimokawa if (rdch && ret == 0) { 696111615Ssimokawa CHN_LOCK(rdch); 697111615Ssimokawa ret = chn_setspeed(rdch, *arg_i); 698111615Ssimokawa if (tmp == 0) 699122387Ssimokawa tmp = rdch->speed; 700122387Ssimokawa CHN_UNLOCK(rdch); 701122387Ssimokawa } 702122387Ssimokawa *arg_i = tmp; 703122387Ssimokawa break; 704122387Ssimokawa 705122387Ssimokawa case SOUND_PCM_READ_RATE: 706122387Ssimokawa *arg_i = wrch? wrch->speed : rdch->speed; 707122387Ssimokawa break; 708122387Ssimokawa 709122387Ssimokawa case SNDCTL_DSP_STEREO: 710122387Ssimokawa tmp = -1; 711122387Ssimokawa *arg_i = (*arg_i)? AFMT_STEREO : 0; 712122387Ssimokawa if (wrch) { 713122387Ssimokawa CHN_LOCK(wrch); 714153706Strhodes ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i); 715153706Strhodes tmp = (wrch->format & AFMT_STEREO)? 1 : 0; 716122387Ssimokawa CHN_UNLOCK(wrch); 717122387Ssimokawa } 718122387Ssimokawa if (rdch && ret == 0) { 719122387Ssimokawa CHN_LOCK(rdch); 720122387Ssimokawa ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i); 721114069Ssimokawa if (tmp == -1) 722114069Ssimokawa tmp = (rdch->format & AFMT_STEREO)? 1 : 0; 723111615Ssimokawa CHN_UNLOCK(rdch); 724111615Ssimokawa } 725111615Ssimokawa *arg_i = tmp; 726111615Ssimokawa break; 727111615Ssimokawa 728188756Ssbruno case SOUND_PCM_WRITE_CHANNELS: 729103285Sikob/* case SNDCTL_DSP_CHANNELS: ( == SOUND_PCM_WRITE_CHANNELS) */ 730111615Ssimokawa if (*arg_i != 0) { 731103285Sikob tmp = 0; 732111615Ssimokawa *arg_i = (*arg_i != 1)? AFMT_STEREO : 0; 733103285Sikob if (wrch) { 734188756Ssbruno CHN_LOCK(wrch); 735188756Ssbruno ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i); 736188756Ssbruno tmp = (wrch->format & AFMT_STEREO)? 2 : 1; 737103285Sikob CHN_UNLOCK(wrch); 738103285Sikob } 739103285Sikob if (rdch && ret == 0) { 740121185Ssimokawa CHN_LOCK(rdch); 741121185Ssimokawa ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i); 742111615Ssimokawa if (tmp == 0) 743103285Sikob tmp = (rdch->format & AFMT_STEREO)? 2 : 1; 744121185Ssimokawa CHN_UNLOCK(rdch); 745121185Ssimokawa } 746121185Ssimokawa *arg_i = tmp; 747103285Sikob } else { 748103285Sikob *arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 2 : 1; 749170374Ssimokawa } 750103285Sikob break; 751111615Ssimokawa 752170374Ssimokawa case SOUND_PCM_READ_CHANNELS: 753103285Sikob *arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 2 : 1; 754111615Ssimokawa break; 755188756Ssbruno 756111615Ssimokawa case SNDCTL_DSP_GETFMTS: /* returns a mask of supported fmts */ 757111615Ssimokawa *arg_i = wrch? chn_getformats(wrch) : chn_getformats(rdch); 758103285Sikob break ; 759110336Ssimokawa 760103285Sikob case SNDCTL_DSP_SETFMT: /* sets _one_ format */ 761113584Ssimokawa /* XXX locking */ 762111615Ssimokawa if ((*arg_i != AFMT_QUERY)) { 763103285Sikob tmp = 0; 764111615Ssimokawa if (wrch) { 765111615Ssimokawa CHN_LOCK(wrch); 766111615Ssimokawa ret = chn_setformat(wrch, (*arg_i) | (wrch->format & AFMT_STEREO)); 767110336Ssimokawa tmp = wrch->format & ~AFMT_STEREO; 768110336Ssimokawa CHN_UNLOCK(wrch); 769111615Ssimokawa } 770110336Ssimokawa if (rdch && ret == 0) { 771103285Sikob CHN_LOCK(rdch); 772103285Sikob ret = chn_setformat(rdch, (*arg_i) | (rdch->format & AFMT_STEREO)); 773103285Sikob if (tmp == 0) 774103285Sikob tmp = rdch->format & ~AFMT_STEREO; 775103285Sikob CHN_UNLOCK(rdch); 776103285Sikob } 777188756Ssbruno *arg_i = tmp; 778188756Ssbruno } else 779103285Sikob *arg_i = (wrch? wrch->format : rdch->format) & ~AFMT_STEREO; 780111615Ssimokawa break; 781170374Ssimokawa 782103285Sikob case SNDCTL_DSP_SETFRAGMENT: 783111615Ssimokawa /* XXX locking */ 784170374Ssimokawa DEB(printf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg)); 785111615Ssimokawa { 786103285Sikob u_int32_t fragln = (*arg_i) & 0x0000ffff; 787187993Ssbruno u_int32_t maxfrags = ((*arg_i) & 0xffff0000) >> 16; 788187993Ssbruno u_int32_t fragsz; 789187993Ssbruno 790103285Sikob RANGE(fragln, 4, 16); 791103285Sikob fragsz = 1 << fragln; 792103285Sikob 793103285Sikob if (maxfrags == 0) 794103285Sikob maxfrags = CHN_2NDBUFMAXSIZE / fragsz; 795103285Sikob if (maxfrags < 2) { 796103285Sikob ret = EINVAL; 797103285Sikob break; 798103285Sikob } 799103285Sikob if (maxfrags * fragsz > CHN_2NDBUFMAXSIZE) 800103285Sikob maxfrags = CHN_2NDBUFMAXSIZE / fragsz; 801103285Sikob 802103285Sikob DEB(printf("SNDCTL_DSP_SETFRAGMENT %d frags, %d sz\n", maxfrags, fragsz)); 803103285Sikob if (rdch) { 804103285Sikob CHN_LOCK(rdch); 805113584Ssimokawa ret = chn_setblocksize(rdch, maxfrags, fragsz); 806113584Ssimokawa maxfrags = sndbuf_getblkcnt(rdch->bufsoft); 807113584Ssimokawa fragsz = sndbuf_getblksz(rdch->bufsoft); 808113584Ssimokawa CHN_UNLOCK(rdch); 809113584Ssimokawa } 810113584Ssimokawa if (wrch && ret == 0) { 811113584Ssimokawa CHN_LOCK(wrch); 812113584Ssimokawa ret = chn_setblocksize(wrch, maxfrags, fragsz); 813124145Ssimokawa maxfrags = sndbuf_getblkcnt(wrch->bufsoft); 814170374Ssimokawa fragsz = sndbuf_getblksz(wrch->bufsoft); 815124145Ssimokawa CHN_UNLOCK(wrch); 816124145Ssimokawa } 817170374Ssimokawa 818124145Ssimokawa fragln = 0; 819122387Ssimokawa while (fragsz > 1) { 820113584Ssimokawa fragln++; 821113584Ssimokawa fragsz >>= 1; 822113584Ssimokawa } 823103285Sikob *arg_i = (maxfrags << 16) | fragln; 824103285Sikob } 825103285Sikob break; 826103285Sikob 827103285Sikob case SNDCTL_DSP_GETISPACE: 828103285Sikob /* return the size of data available in the input queue */ 829103285Sikob { 830111199Ssimokawa audio_buf_info *a = (audio_buf_info *)arg; 831111199Ssimokawa if (rdch) { 832103285Sikob struct snd_dbuf *bs = rdch->bufsoft; 833170400Ssimokawa 834170400Ssimokawa CHN_LOCK(rdch); 835170400Ssimokawa a->bytes = sndbuf_getready(bs); 836170400Ssimokawa a->fragments = a->bytes / sndbuf_getblksz(bs); 837122387Ssimokawa a->fragstotal = sndbuf_getblkcnt(bs); 838122387Ssimokawa a->fragsize = sndbuf_getblksz(bs); 839122387Ssimokawa CHN_UNLOCK(rdch); 840122387Ssimokawa } 841122387Ssimokawa } 842122387Ssimokawa break; 843122387Ssimokawa 844111615Ssimokawa case SNDCTL_DSP_GETOSPACE: 845111615Ssimokawa /* return space available in the output queue */ 846103285Sikob { 847111615Ssimokawa audio_buf_info *a = (audio_buf_info *)arg; 848130532Sdfr if (wrch) { 849103285Sikob struct snd_dbuf *bs = wrch->bufsoft; 850103285Sikob 851110193Ssimokawa CHN_LOCK(wrch); 852110193Ssimokawa chn_wrupdate(wrch); 853110193Ssimokawa a->bytes = sndbuf_getfree(bs); 854121185Ssimokawa a->fragments = a->bytes / sndbuf_getblksz(bs); 855103285Sikob a->fragstotal = sndbuf_getblkcnt(bs); 856110145Ssimokawa a->fragsize = sndbuf_getblksz(bs); 857121185Ssimokawa CHN_UNLOCK(wrch); 858103285Sikob } 859103285Sikob } 860103285Sikob break; 861110193Ssimokawa 862103285Sikob case SNDCTL_DSP_GETIPTR: 863188756Ssbruno { 864188756Ssbruno count_info *a = (count_info *)arg; 865188756Ssbruno if (rdch) { 866188756Ssbruno struct snd_dbuf *bs = rdch->bufsoft; 867103285Sikob 868111615Ssimokawa CHN_LOCK(rdch); 869103285Sikob chn_rdupdate(rdch); 870103285Sikob a->bytes = sndbuf_gettotal(bs); 871103285Sikob a->blocks = sndbuf_getblocks(bs) - rdch->blocks; 872103285Sikob a->ptr = sndbuf_getreadyptr(bs); 873103285Sikob rdch->blocks = sndbuf_getblocks(bs); 874103285Sikob CHN_UNLOCK(rdch); 875103285Sikob } else 876103285Sikob ret = EINVAL; 877103285Sikob } 878103285Sikob break; 879103285Sikob 880103285Sikob case SNDCTL_DSP_GETOPTR: 881103285Sikob { 882103285Sikob count_info *a = (count_info *)arg; 883103285Sikob if (wrch) { 884103285Sikob struct snd_dbuf *bs = wrch->bufsoft; 885103285Sikob 886111615Ssimokawa CHN_LOCK(wrch); 887121185Ssimokawa chn_wrupdate(wrch); 888121185Ssimokawa a->bytes = sndbuf_gettotal(bs); 889103285Sikob a->blocks = sndbuf_getblocks(bs) - wrch->blocks; 890170374Ssimokawa a->ptr = sndbuf_getreadyptr(bs); 891124145Ssimokawa wrch->blocks = sndbuf_getblocks(bs); 892124145Ssimokawa CHN_UNLOCK(wrch); 893170374Ssimokawa } else 894103285Sikob ret = EINVAL; 895103285Sikob } 896103285Sikob break; 897103285Sikob 898103285Sikob case SNDCTL_DSP_GETCAPS: 899113584Ssimokawa *arg_i = DSP_CAP_REALTIME | DSP_CAP_MMAP | DSP_CAP_TRIGGER; 900103285Sikob if (rdch && wrch && !(dsp_get_flags(i_dev) & SD_F_SIMPLEX)) 901103285Sikob *arg_i |= DSP_CAP_DUPLEX; 902113584Ssimokawa break; 903188756Ssbruno 904103285Sikob case SOUND_PCM_READ_BITS: 905113584Ssimokawa *arg_i = ((wrch? wrch->format : rdch->format) & AFMT_16BIT)? 16 : 8; 906113584Ssimokawa break; 907113584Ssimokawa 908113584Ssimokawa case SNDCTL_DSP_SETTRIGGER: 909103285Sikob if (rdch) { 910103285Sikob CHN_LOCK(rdch); 911103285Sikob rdch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER); 912103285Sikob if (*arg_i & PCM_ENABLE_INPUT) 913113584Ssimokawa chn_start(rdch, 1); 914113584Ssimokawa else 915113584Ssimokawa rdch->flags |= CHN_F_NOTRIGGER; 916113584Ssimokawa CHN_UNLOCK(rdch); 917113584Ssimokawa } 918113584Ssimokawa if (wrch) { 919113584Ssimokawa CHN_LOCK(wrch); 920113584Ssimokawa wrch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER); 921113584Ssimokawa if (*arg_i & PCM_ENABLE_OUTPUT) 922170374Ssimokawa chn_start(wrch, 1); 923113584Ssimokawa else 924170374Ssimokawa wrch->flags |= CHN_F_NOTRIGGER; 925113584Ssimokawa CHN_UNLOCK(wrch); 926113584Ssimokawa } 927113584Ssimokawa break; 928103285Sikob 929114732Ssimokawa case SNDCTL_DSP_GETTRIGGER: 930103285Sikob *arg_i = 0; 931114732Ssimokawa if (wrch && wrch->flags & CHN_F_TRIGGERED) 932114732Ssimokawa *arg_i |= PCM_ENABLE_OUTPUT; 933114732Ssimokawa if (rdch && rdch->flags & CHN_F_TRIGGERED) 934114732Ssimokawa *arg_i |= PCM_ENABLE_INPUT; 935114732Ssimokawa break; 936188756Ssbruno 937188756Ssbruno case SNDCTL_DSP_GETODELAY: 938114732Ssimokawa if (wrch) { 939114732Ssimokawa struct snd_dbuf *b = wrch->bufhard; 940114732Ssimokawa struct snd_dbuf *bs = wrch->bufsoft; 941121185Ssimokawa 942121185Ssimokawa CHN_LOCK(wrch); 943121185Ssimokawa chn_wrupdate(wrch); 944114732Ssimokawa *arg_i = sndbuf_getready(b) + sndbuf_getready(bs); 945114732Ssimokawa CHN_UNLOCK(wrch); 946114732Ssimokawa } else 947114732Ssimokawa ret = EINVAL; 948114732Ssimokawa break; 949114732Ssimokawa 950114732Ssimokawa case SNDCTL_DSP_POST: 951114732Ssimokawa if (wrch) { 952114732Ssimokawa CHN_LOCK(wrch); 953114732Ssimokawa wrch->flags &= ~CHN_F_NOTRIGGER; 954188756Ssbruno chn_start(wrch, 1); 955188756Ssbruno CHN_UNLOCK(wrch); 956114732Ssimokawa } 957114732Ssimokawa break; 958114732Ssimokawa 959167632Ssimokawa case SNDCTL_DSP_MAPINBUF: 960120660Ssimokawa case SNDCTL_DSP_MAPOUTBUF: 961114732Ssimokawa case SNDCTL_DSP_SETSYNCRO: 962114732Ssimokawa /* undocumented */ 963114732Ssimokawa 964114732Ssimokawa case SNDCTL_DSP_SUBDIVIDE: 965114732Ssimokawa case SOUND_PCM_WRITE_FILTER: 966114732Ssimokawa case SOUND_PCM_READ_FILTER: 967114732Ssimokawa /* dunno what these do, don't sound important */ 968114732Ssimokawa default: 969114732Ssimokawa DEB(printf("default ioctl fn 0x%08lx fail\n", cmd)); 970103285Sikob ret = EINVAL; 971114732Ssimokawa break; 972114732Ssimokawa } 973103285Sikob relchns(i_dev, rdch, wrch, 0); 974114732Ssimokawa splx(s); 975114732Ssimokawa return ret; 976188756Ssbruno} 977188756Ssbruno 978103285Sikobstatic int 979114732Ssimokawadsp_poll(dev_t i_dev, int events, struct thread *td) 980113584Ssimokawa{ 981103285Sikob struct pcm_channel *wrch = NULL, *rdch = NULL; 982103285Sikob intrmask_t s; 983103285Sikob int ret, e; 984111615Ssimokawa 985111615Ssimokawa s = spltty(); 986111615Ssimokawa ret = 0; 987121185Ssimokawa getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); 988111615Ssimokawa 989111615Ssimokawa if (wrch) { 990121185Ssimokawa e = (events & (POLLOUT | POLLWRNORM)); 991121185Ssimokawa if (e) 992121185Ssimokawa ret |= chn_poll(wrch, e, td); 993121185Ssimokawa } 994121185Ssimokawa if (rdch) { 995111615Ssimokawa e = (events & (POLLIN | POLLRDNORM)); 996111615Ssimokawa if (e) 997111615Ssimokawa ret |= chn_poll(rdch, e, td); 998103285Sikob } 999111615Ssimokawa relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); 1000103285Sikob 1001111615Ssimokawa splx(s); 1002103285Sikob return ret; 1003111615Ssimokawa} 1004103285Sikob 1005111615Ssimokawastatic int 1006110269Ssimokawadsp_mmap(dev_t i_dev, vm_offset_t offset, int nprot) 1007188756Ssbruno{ 1008188756Ssbruno struct pcm_channel *wrch = NULL, *rdch = NULL, *c; 1009103285Sikob intrmask_t s; 1010111615Ssimokawa int ret; 1011111615Ssimokawa 1012111615Ssimokawa if (nprot & PROT_EXEC) 1013188756Ssbruno return -1; 1014188756Ssbruno 1015111615Ssimokawa s = spltty(); 1016111615Ssimokawa getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); 1017111615Ssimokawa#if 0 1018111615Ssimokawa /* 1019111615Ssimokawa * XXX the linux api uses the nprot to select read/write buffer 1020111615Ssimokawa * our vm system doesn't allow this, so force write buffer 1021111615Ssimokawa */ 1022111615Ssimokawa 1023111615Ssimokawa if (wrch && (nprot & PROT_WRITE)) { 1024111615Ssimokawa c = wrch; 1025111615Ssimokawa } else if (rdch && (nprot & PROT_READ)) { 1026111615Ssimokawa c = rdch; 1027103285Sikob } else { 1028103285Sikob splx(s); 1029103285Sikob return -1; 1030111615Ssimokawa } 1031103285Sikob#else 1032111615Ssimokawa c = wrch; 1033111615Ssimokawa#endif 1034110798Ssimokawa 1035103285Sikob if (c == NULL) { 1036111615Ssimokawa relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); 1037111615Ssimokawa splx(s); 1038111615Ssimokawa return -1; 1039111615Ssimokawa } 1040110798Ssimokawa 1041110798Ssimokawa if (offset >= sndbuf_getsize(c->bufsoft)) { 1042103285Sikob relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); 1043188756Ssbruno splx(s); 1044188756Ssbruno return -1; 1045103285Sikob } 1046111615Ssimokawa 1047111615Ssimokawa if (!(c->flags & CHN_F_MAPPED)) 1048111615Ssimokawa c->flags |= CHN_F_MAPPED; 1049111615Ssimokawa 1050111615Ssimokawa ret = atop(vtophys(sndbuf_getbufofs(c->bufsoft, offset))); 1051111615Ssimokawa relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); 1052103285Sikob 1053111615Ssimokawa splx(s); 1054111615Ssimokawa return ret; 1055103285Sikob} 1056103285Sikob 1057103285Sikobint 1058103285Sikobdsp_register(int unit, int channel) 1059170374Ssimokawa{ 1060111040Ssimokawa make_dev(&dsp_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP, channel), 1061111615Ssimokawa UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", unit, channel); 1062111615Ssimokawa make_dev(&dsp_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP16, channel), 1063170374Ssimokawa UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", unit, channel); 1064103285Sikob make_dev(&dsp_cdevsw, PCMMKMINOR(unit, SND_DEV_AUDIO, channel), 1065103285Sikob UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", unit, channel); 1066111615Ssimokawa 1067111615Ssimokawa return 0; 1068111615Ssimokawa} 1069111040Ssimokawa 1070122387Ssimokawaint 1071111615Ssimokawadsp_registerrec(int unit, int channel) 1072103285Sikob{ 1073103285Sikob make_dev(&dsp_cdevsw, PCMMKMINOR(unit, SND_DEV_DSPREC, channel), 1074103285Sikob UID_ROOT, GID_WHEEL, 0666, "dspr%d.%d", unit, channel); 1075103285Sikob 1076103285Sikob return 0; 1077103285Sikob} 1078111615Ssimokawa 1079111615Ssimokawaint 1080103285Sikobdsp_unregister(int unit, int channel) 1081103285Sikob{ 1082111615Ssimokawa dev_t pdev; 1083111615Ssimokawa 1084103285Sikob pdev = makedev(SND_CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, channel)); 1085188756Ssbruno destroy_dev(pdev); 1086188756Ssbruno pdev = makedev(SND_CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, channel)); 1087103285Sikob destroy_dev(pdev); 1088113584Ssimokawa pdev = makedev(SND_CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, channel)); 1089111199Ssimokawa destroy_dev(pdev); 1090103285Sikob 1091249468Smav return 0; 1092111615Ssimokawa} 1093111615Ssimokawa 1094103285Sikobint 1095111199Ssimokawadsp_unregisterrec(int unit, int channel) 1096111199Ssimokawa{ 1097111199Ssimokawa dev_t pdev; 1098111199Ssimokawa 1099111199Ssimokawa pdev = makedev(SND_CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSPREC, channel)); 1100111199Ssimokawa destroy_dev(pdev); 1101111615Ssimokawa 1102111199Ssimokawa return 0; 1103111199Ssimokawa} 1104111199Ssimokawa 1105111615Ssimokawa#ifdef USING_DEVFS 1106103285Sikobstatic void 1107103285Sikobdsp_clone(void *arg, char *name, int namelen, dev_t *dev) 1108103285Sikob{ 1109103285Sikob dev_t pdev; 1110103285Sikob int i, cont, unit, devtype; 1111103285Sikob int devtypes[3] = {SND_DEV_DSP, SND_DEV_DSP16, SND_DEV_AUDIO}; 1112103285Sikob char *devnames[3] = {"dsp", "dspW", "audio"}; 1113103285Sikob 1114103285Sikob if (*dev != NODEV) 1115103285Sikob return; 1116188756Ssbruno if (pcm_devclass == NULL) 1117188756Ssbruno return; 1118103285Sikob 1119114732Ssimokawa devtype = 0; 1120188756Ssbruno unit = -1; 1121188756Ssbruno for (i = 0; (i < 3) && (unit == -1); i++) { 1122114732Ssimokawa devtype = devtypes[i]; 1123114732Ssimokawa if (strcmp(name, devnames[i]) == 0) { 1124113584Ssimokawa unit = snd_unit; 1125111615Ssimokawa } else { 1126170374Ssimokawa if (dev_stdclone(name, NULL, devnames[i], &unit) != 1) 1127111615Ssimokawa unit = -1; 1128111615Ssimokawa } 1129170374Ssimokawa } 1130111615Ssimokawa if (unit == -1 || unit >= devclass_get_maxunit(pcm_devclass)) 1131103285Sikob return; 1132103285Sikob 1133103285Sikob cont = 1; 1134110336Ssimokawa for (i = 0; cont; i++) { 1135103285Sikob pdev = makedev(SND_CDEV_MAJOR, PCMMKMINOR(unit, devtype, i)); 1136103285Sikob if (pdev->si_flags & SI_NAMED) { 1137103285Sikob if ((pdev->si_drv1 == NULL) && (pdev->si_drv2 == NULL)) { 1138103285Sikob *dev = pdev; 1139103285Sikob return; 1140188756Ssbruno } 1141188756Ssbruno } else { 1142103285Sikob cont = 0; 1143103285Sikob } 1144103285Sikob } 1145103285Sikob} 1146115788Ssimokawa 1147167632Ssimokawastatic void 1148110336Ssimokawadsp_sysinit(void *p) 1149167632Ssimokawa{ 1150120660Ssimokawa dsp_ehtag = EVENTHANDLER_REGISTER(dev_clone, dsp_clone, 0, 1000); 1151103285Sikob} 1152103285Sikob 1153111615Ssimokawastatic void 1154103285Sikobdsp_sysuninit(void *p) 1155103285Sikob{ 1156103285Sikob if (dsp_ehtag != NULL) 1157103285Sikob EVENTHANDLER_DEREGISTER(dev_clone, dsp_ehtag); 1158103285Sikob} 1159103285Sikob 1160103285SikobSYSINIT(dsp_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysinit, NULL); 1161103285SikobSYSUNINIT(dsp_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysuninit, NULL); 1162103285Sikob#endif 1163188756Ssbruno 1164188756Ssbruno 1165103285Sikob