| Commit | Line | Data |
|---|---|---|
| 558a398b SS |
1 | /*- |
| 2 | * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> | |
| 984263bc MD |
3 | * Portions Copyright by Luigi Rizzo - 1997-99 |
| 4 | * All rights reserved. | |
| 5 | * | |
| 6 | * Redistribution and use in source and binary forms, with or without | |
| 7 | * modification, are permitted provided that the following conditions | |
| 8 | * are met: | |
| 9 | * 1. Redistributions of source code must retain the above copyright | |
| 10 | * notice, this list of conditions and the following disclaimer. | |
| 11 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 12 | * notice, this list of conditions and the following disclaimer in the | |
| 13 | * documentation and/or other materials provided with the distribution. | |
| 14 | * | |
| 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
| 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
| 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 25 | * SUCH DAMAGE. | |
| 1de703da | 26 | * |
| 4886ec58 | 27 | * $FreeBSD: src/sys/dev/sound/pcm/channel.c,v 1.99.2.5 2007/05/13 20:53:39 ariff Exp $ |
| 5a5410b8 | 28 | * $DragonFly: src/sys/dev/sound/pcm/channel.c,v 1.15 2008/01/05 13:34:22 corecode Exp $ |
| 984263bc MD |
29 | */ |
| 30 | ||
| 558a398b SS |
31 | #include "use_isa.h" |
| 32 | ||
| 984263bc | 33 | #include <dev/sound/pcm/sound.h> |
| 558a398b | 34 | #include <sys/vnode.h> /* IO_NDELAY */ |
| 984263bc MD |
35 | |
| 36 | #include "feeder_if.h" | |
| 37 | ||
| 5a5410b8 | 38 | SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/channel.c,v 1.15 2008/01/05 13:34:22 corecode Exp $"); |
| 984263bc MD |
39 | |
| 40 | #define MIN_CHUNK_SIZE 256 /* for uiomove etc. */ | |
| 558a398b | 41 | #if 0 |
| 984263bc MD |
42 | #define DMA_ALIGN_THRESHOLD 4 |
| 43 | #define DMA_ALIGN_MASK (~(DMA_ALIGN_THRESHOLD - 1)) | |
| 558a398b | 44 | #endif |
| 984263bc | 45 | |
| 984263bc MD |
46 | #define CANCHANGE(c) (!(c->flags & CHN_F_TRIGGERED)) |
| 47 | ||
| 48 | /* | |
| 49 | #define DEB(x) x | |
| 50 | */ | |
| 51 | ||
| 52 | static int chn_targetirqrate = 32; | |
| 53 | TUNABLE_INT("hw.snd.targetirqrate", &chn_targetirqrate); | |
| 54 | ||
| 55 | static int | |
| 56 | sysctl_hw_snd_targetirqrate(SYSCTL_HANDLER_ARGS) | |
| 57 | { | |
| 58 | int err, val; | |
| 59 | ||
| 60 | val = chn_targetirqrate; | |
| 61 | err = sysctl_handle_int(oidp, &val, sizeof(val), req); | |
| 62 | if (val < 16 || val > 512) | |
| 63 | err = EINVAL; | |
| 64 | else | |
| 65 | chn_targetirqrate = val; | |
| 66 | ||
| 67 | return err; | |
| 68 | } | |
| 69 | SYSCTL_PROC(_hw_snd, OID_AUTO, targetirqrate, CTLTYPE_INT | CTLFLAG_RW, | |
| 70 | 0, sizeof(int), sysctl_hw_snd_targetirqrate, "I", ""); | |
| 71 | static int report_soft_formats = 1; | |
| 72 | SYSCTL_INT(_hw_snd, OID_AUTO, report_soft_formats, CTLFLAG_RW, | |
| 73 | &report_soft_formats, 1, "report software-emulated formats"); | |
| 74 | ||
| 75 | static int chn_buildfeeder(struct pcm_channel *c); | |
| 76 | ||
| 77 | static void | |
| 558a398b | 78 | chn_lockinit(struct pcm_channel *c, int dir) |
| 984263bc | 79 | { |
| 558a398b SS |
80 | switch(dir) { |
| 81 | case PCMDIR_PLAY: | |
| 82 | c->lock = snd_mtxcreate(c->name, "pcm play channel"); | |
| 83 | break; | |
| 84 | case PCMDIR_REC: | |
| 85 | c->lock = snd_mtxcreate(c->name, "pcm record channel"); | |
| 86 | break; | |
| 87 | case PCMDIR_VIRTUAL: | |
| 88 | c->lock = snd_mtxcreate(c->name, "pcm virtual play channel"); | |
| 89 | break; | |
| 90 | case 0: | |
| 91 | c->lock = snd_mtxcreate(c->name, "pcm fake channel"); | |
| 92 | break; | |
| 93 | } | |
| 984263bc MD |
94 | } |
| 95 | ||
| 96 | static void | |
| 97 | chn_lockdestroy(struct pcm_channel *c) | |
| 98 | { | |
| 99 | snd_mtxfree(c->lock); | |
| 100 | } | |
| 101 | ||
| 102 | static int | |
| 103 | chn_polltrigger(struct pcm_channel *c) | |
| 104 | { | |
| 105 | struct snd_dbuf *bs = c->bufsoft; | |
| 106 | unsigned amt, lim; | |
| 107 | ||
| 108 | CHN_LOCKASSERT(c); | |
| 109 | if (c->flags & CHN_F_MAPPED) { | |
| 110 | if (sndbuf_getprevblocks(bs) == 0) | |
| 111 | return 1; | |
| 112 | else | |
| 113 | return (sndbuf_getblocks(bs) > sndbuf_getprevblocks(bs))? 1 : 0; | |
| 114 | } else { | |
| 115 | amt = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(bs) : sndbuf_getready(bs); | |
| 558a398b | 116 | #if 0 |
| 984263bc | 117 | lim = (c->flags & CHN_F_HAS_SIZE)? sndbuf_getblksz(bs) : 1; |
| 558a398b | 118 | #endif |
| 984263bc MD |
119 | lim = 1; |
| 120 | return (amt >= lim)? 1 : 0; | |
| 121 | } | |
| 122 | return 0; | |
| 123 | } | |
| 124 | ||
| 125 | static int | |
| 126 | chn_pollreset(struct pcm_channel *c) | |
| 127 | { | |
| 128 | struct snd_dbuf *bs = c->bufsoft; | |
| 129 | ||
| 130 | CHN_LOCKASSERT(c); | |
| 131 | sndbuf_updateprevtotal(bs); | |
| 132 | return 1; | |
| 133 | } | |
| 134 | ||
| 135 | static void | |
| 136 | chn_wakeup(struct pcm_channel *c) | |
| 137 | { | |
| 558a398b SS |
138 | struct snd_dbuf *bs = c->bufsoft; |
| 139 | struct pcmchan_children *pce; | |
| 984263bc MD |
140 | |
| 141 | CHN_LOCKASSERT(c); | |
| 558a398b SS |
142 | if (SLIST_EMPTY(&c->children)) { |
| 143 | /*if (SEL_WAITING(sndbuf_getsel(bs)) && chn_polltrigger(c))*/ | |
| 5a5410b8 SS |
144 | if (sndbuf_getsel(bs)->si_pid && chn_polltrigger(c)) { |
| 145 | /* | |
| 146 | * We would call selwakeup() here, but as we | |
| 147 | * are in interrupt context, we'd have to | |
| 148 | * aquire the MP lock before. | |
| 149 | * Instead, we'll queue a task in a software | |
| 150 | * interrupt, which will run with the MP lock | |
| 151 | * held. | |
| 152 | * | |
| 153 | * buffer.c:sndbuf_seltask will then call | |
| 154 | * selwakeup() from safer context. | |
| 155 | */ | |
| 156 | taskqueue_enqueue(taskqueue_swi, &bs->seltask); | |
| 157 | } | |
| 558a398b SS |
158 | } else { |
| 159 | SLIST_FOREACH(pce, &c->children, link) { | |
| 160 | CHN_LOCK(pce->channel); | |
| 161 | chn_wakeup(pce->channel); | |
| 162 | CHN_UNLOCK(pce->channel); | |
| 163 | } | |
| 164 | } | |
| 165 | ||
| 984263bc MD |
166 | wakeup(bs); |
| 167 | } | |
| 168 | ||
| 169 | static int | |
| 170 | chn_sleep(struct pcm_channel *c, char *str, int timeout) | |
| 171 | { | |
| 172 | struct snd_dbuf *bs = c->bufsoft; | |
| 173 | int ret; | |
| 174 | ||
| 175 | CHN_LOCKASSERT(c); | |
| 176 | #ifdef USING_MUTEX | |
| cad195a6 | 177 | ret = snd_mtxsleep(bs, c->lock, PCATCH, str, timeout); |
| 984263bc | 178 | #else |
| 558a398b | 179 | ret = tsleep(bs, PRIBIO | PCATCH, str, timeout); |
| 984263bc MD |
180 | #endif |
| 181 | ||
| 182 | return ret; | |
| 183 | } | |
| 184 | ||
| 185 | /* | |
| 186 | * chn_dmaupdate() tracks the status of a dma transfer, | |
| 558a398b | 187 | * updating pointers. |
| 984263bc MD |
188 | */ |
| 189 | ||
| 190 | static unsigned int | |
| 191 | chn_dmaupdate(struct pcm_channel *c) | |
| 192 | { | |
| 193 | struct snd_dbuf *b = c->bufhard; | |
| 194 | unsigned int delta, old, hwptr, amt; | |
| 195 | ||
| 196 | KASSERT(sndbuf_getsize(b) > 0, ("bufsize == 0")); | |
| 197 | CHN_LOCKASSERT(c); | |
| 198 | ||
| 199 | old = sndbuf_gethwptr(b); | |
| 200 | hwptr = chn_getptr(c); | |
| 201 | delta = (sndbuf_getsize(b) + hwptr - old) % sndbuf_getsize(b); | |
| 202 | sndbuf_sethwptr(b, hwptr); | |
| 203 | ||
| 204 | DEB( | |
| 205 | if (delta >= ((sndbuf_getsize(b) * 15) / 16)) { | |
| 206 | if (!(c->flags & (CHN_F_CLOSING | CHN_F_ABORTING))) | |
| 207 | device_printf(c->dev, "hwptr went backwards %d -> %d\n", old, hwptr); | |
| 208 | } | |
| 209 | ); | |
| 210 | ||
| 211 | if (c->direction == PCMDIR_PLAY) { | |
| 212 | amt = MIN(delta, sndbuf_getready(b)); | |
| 213 | if (amt > 0) | |
| 214 | sndbuf_dispose(b, NULL, amt); | |
| 215 | } else { | |
| 216 | amt = MIN(delta, sndbuf_getfree(b)); | |
| 217 | if (amt > 0) | |
| 218 | sndbuf_acquire(b, NULL, amt); | |
| 219 | } | |
| 220 | ||
| 221 | return delta; | |
| 222 | } | |
| 223 | ||
| 224 | void | |
| 225 | chn_wrupdate(struct pcm_channel *c) | |
| 226 | { | |
| 227 | int ret; | |
| 228 | ||
| 229 | CHN_LOCKASSERT(c); | |
| 230 | KASSERT(c->direction == PCMDIR_PLAY, ("chn_wrupdate on bad channel")); | |
| 231 | ||
| 232 | if ((c->flags & (CHN_F_MAPPED | CHN_F_VIRTUAL)) || !(c->flags & CHN_F_TRIGGERED)) | |
| 233 | return; | |
| 234 | chn_dmaupdate(c); | |
| 235 | ret = chn_wrfeed(c); | |
| 236 | /* tell the driver we've updated the primary buffer */ | |
| 237 | chn_trigger(c, PCMTRIG_EMLDMAWR); | |
| 238 | DEB(if (ret) | |
| e3869ec7 | 239 | kprintf("chn_wrupdate: chn_wrfeed returned %d\n", ret);) |
| 984263bc MD |
240 | |
| 241 | } | |
| 242 | ||
| 243 | int | |
| 244 | chn_wrfeed(struct pcm_channel *c) | |
| 245 | { | |
| 246 | struct snd_dbuf *b = c->bufhard; | |
| 247 | struct snd_dbuf *bs = c->bufsoft; | |
| 248 | unsigned int ret, amt; | |
| 249 | ||
| 250 | CHN_LOCKASSERT(c); | |
| 558a398b | 251 | #if 0 |
| 984263bc MD |
252 | DEB( |
| 253 | if (c->flags & CHN_F_CLOSING) { | |
| 254 | sndbuf_dump(b, "b", 0x02); | |
| 255 | sndbuf_dump(bs, "bs", 0x02); | |
| 256 | }) | |
| 558a398b | 257 | #endif |
| 984263bc MD |
258 | |
| 259 | if (c->flags & CHN_F_MAPPED) | |
| 260 | sndbuf_acquire(bs, NULL, sndbuf_getfree(bs)); | |
| 261 | ||
| 262 | amt = sndbuf_getfree(b); | |
| 558a398b SS |
263 | KASSERT(amt <= sndbuf_getsize(bs), |
| 264 | ("%s(%s): amt %d > source size %d, flags 0x%x", __func__, c->name, | |
| 265 | amt, sndbuf_getsize(bs), c->flags)); | |
| 266 | ||
| 267 | ret = (amt > 0) ? sndbuf_feed(bs, b, c, c->feeder, amt) : ENOSPC; | |
| 268 | /* | |
| 269 | * Possible xruns. There should be no empty space left in buffer. | |
| 270 | */ | |
| 271 | if (sndbuf_getfree(b) > 0) | |
| 984263bc MD |
272 | c->xruns++; |
| 273 | ||
| 984263bc MD |
274 | if (ret == 0 && sndbuf_getfree(b) < amt) |
| 275 | chn_wakeup(c); | |
| 276 | ||
| 277 | return ret; | |
| 278 | } | |
| 279 | ||
| 280 | static void | |
| 281 | chn_wrintr(struct pcm_channel *c) | |
| 282 | { | |
| 283 | int ret; | |
| 284 | ||
| 285 | CHN_LOCKASSERT(c); | |
| 286 | /* update pointers in primary buffer */ | |
| 287 | chn_dmaupdate(c); | |
| 288 | /* ...and feed from secondary to primary */ | |
| 289 | ret = chn_wrfeed(c); | |
| 290 | /* tell the driver we've updated the primary buffer */ | |
| 291 | chn_trigger(c, PCMTRIG_EMLDMAWR); | |
| 292 | DEB(if (ret) | |
| e3869ec7 | 293 | kprintf("chn_wrintr: chn_wrfeed returned %d\n", ret);) |
| 984263bc MD |
294 | } |
| 295 | ||
| 296 | /* | |
| 297 | * user write routine - uiomove data into secondary buffer, trigger if necessary | |
| 298 | * if blocking, sleep, rinse and repeat. | |
| 299 | * | |
| 300 | * called externally, so must handle locking | |
| 301 | */ | |
| 302 | ||
| 303 | int | |
| 9ba76b73 | 304 | chn_write(struct pcm_channel *c, struct uio *buf, int ioflags) |
| 984263bc MD |
305 | { |
| 306 | int ret, timeout, newsize, count, sz; | |
| 9ba76b73 | 307 | int nbio; |
| 984263bc | 308 | struct snd_dbuf *bs = c->bufsoft; |
| 558a398b SS |
309 | void *off; |
| 310 | int t, x,togo,p; | |
| 984263bc MD |
311 | |
| 312 | CHN_LOCKASSERT(c); | |
| 313 | /* | |
| 314 | * XXX Certain applications attempt to write larger size | |
| 315 | * of pcm data than c->blocksize2nd without blocking, | |
| 316 | * resulting partial write. Expand the block size so that | |
| 317 | * the write operation avoids blocking. | |
| 318 | */ | |
| 558a398b | 319 | nbio = (c->flags & CHN_F_NBIO) || (ioflags & IO_NDELAY); |
| e54488bb MD |
320 | if (nbio && buf->uio_resid > (size_t)sndbuf_getblksz(bs)) { |
| 321 | DEB(device_printf(c->dev, "broken app, nbio and tried to write %ld bytes with fragsz %d\n", | |
| 984263bc MD |
322 | buf->uio_resid, sndbuf_getblksz(bs))); |
| 323 | newsize = 16; | |
| e54488bb | 324 | while (newsize < (int)szmin(buf->uio_resid, CHN_2NDBUFMAXSIZE / 2)) |
| 984263bc MD |
325 | newsize <<= 1; |
| 326 | chn_setblocksize(c, sndbuf_getblkcnt(bs), newsize); | |
| 327 | DEB(device_printf(c->dev, "frags reset to %d x %d\n", sndbuf_getblkcnt(bs), sndbuf_getblksz(bs))); | |
| 328 | } | |
| 329 | ||
| 330 | ret = 0; | |
| 331 | count = hz; | |
| 332 | while (!ret && (buf->uio_resid > 0) && (count > 0)) { | |
| 333 | sz = sndbuf_getfree(bs); | |
| 334 | if (sz == 0) { | |
| 558a398b | 335 | if (nbio) |
| 984263bc | 336 | ret = EWOULDBLOCK; |
| 558a398b | 337 | else { |
| 984263bc MD |
338 | timeout = (hz * sndbuf_getblksz(bs)) / (sndbuf_getspd(bs) * sndbuf_getbps(bs)); |
| 339 | if (timeout < 1) | |
| 340 | timeout = 1; | |
| 341 | timeout = 1; | |
| 342 | ret = chn_sleep(c, "pcmwr", timeout); | |
| 343 | if (ret == EWOULDBLOCK) { | |
| 344 | count -= timeout; | |
| 345 | ret = 0; | |
| 346 | } else if (ret == 0) | |
| 347 | count = hz; | |
| 348 | } | |
| 349 | } else { | |
| e54488bb | 350 | sz = (int)szmin(sz, buf->uio_resid); |
| 984263bc | 351 | KASSERT(sz > 0, ("confusion in chn_write")); |
| e3869ec7 | 352 | /* kprintf("sz: %d\n", sz); */ |
| 558a398b SS |
353 | |
| 354 | /* | |
| 355 | * The following assumes that the free space in | |
| 356 | * the buffer can never be less around the | |
| 357 | * unlock-uiomove-lock sequence. | |
| 358 | */ | |
| 359 | togo = sz; | |
| 360 | while (ret == 0 && togo> 0) { | |
| 361 | p = sndbuf_getfreeptr(bs); | |
| 362 | t = MIN(togo, sndbuf_getsize(bs) - p); | |
| 363 | off = sndbuf_getbufofs(bs, p); | |
| 364 | CHN_UNLOCK(c); | |
| 365 | ret = uiomove(off, t, buf); | |
| 366 | CHN_LOCK(c); | |
| 367 | togo -= t; | |
| 368 | x = sndbuf_acquire(bs, NULL, t); | |
| 369 | } | |
| 370 | ret = 0; | |
| 984263bc MD |
371 | if (ret == 0 && !(c->flags & CHN_F_TRIGGERED)) |
| 372 | chn_start(c, 0); | |
| 373 | } | |
| 374 | } | |
| e3869ec7 | 375 | /* kprintf("ret: %d left: %d\n", ret, buf->uio_resid); */ |
| 984263bc MD |
376 | |
| 377 | if (count <= 0) { | |
| 378 | c->flags |= CHN_F_DEAD; | |
| e3869ec7 | 379 | kprintf("%s: play interrupt timeout, channel dead\n", c->name); |
| 984263bc MD |
380 | } |
| 381 | ||
| 382 | return ret; | |
| 383 | } | |
| 384 | ||
| 558a398b | 385 | #if 0 |
| 984263bc MD |
386 | static int |
| 387 | chn_rddump(struct pcm_channel *c, unsigned int cnt) | |
| 388 | { | |
| 389 | struct snd_dbuf *b = c->bufhard; | |
| 390 | ||
| 391 | CHN_LOCKASSERT(c); | |
| 558a398b SS |
392 | #if 0 |
| 393 | static uint32_t kk = 0; | |
| 394 | printf("%u: dumping %d bytes\n", ++kk, cnt); | |
| 395 | #endif | |
| 396 | c->xruns++; | |
| 984263bc MD |
397 | sndbuf_setxrun(b, sndbuf_getxrun(b) + cnt); |
| 398 | return sndbuf_dispose(b, NULL, cnt); | |
| 399 | } | |
| 558a398b | 400 | #endif |
| 984263bc MD |
401 | |
| 402 | /* | |
| 403 | * Feed new data from the read buffer. Can be called in the bottom half. | |
| 984263bc MD |
404 | */ |
| 405 | int | |
| 406 | chn_rdfeed(struct pcm_channel *c) | |
| 407 | { | |
| 408 | struct snd_dbuf *b = c->bufhard; | |
| 409 | struct snd_dbuf *bs = c->bufsoft; | |
| 410 | unsigned int ret, amt; | |
| 411 | ||
| 412 | CHN_LOCKASSERT(c); | |
| 413 | DEB( | |
| 414 | if (c->flags & CHN_F_CLOSING) { | |
| 415 | sndbuf_dump(b, "b", 0x02); | |
| 416 | sndbuf_dump(bs, "bs", 0x02); | |
| 417 | }) | |
| 418 | ||
| 558a398b | 419 | #if 0 |
| 984263bc MD |
420 | amt = sndbuf_getready(b); |
| 421 | if (sndbuf_getfree(bs) < amt) { | |
| 422 | c->xruns++; | |
| 423 | amt = sndbuf_getfree(bs); | |
| 424 | } | |
| 558a398b SS |
425 | #endif |
| 426 | amt = sndbuf_getfree(bs); | |
| 984263bc MD |
427 | ret = (amt > 0)? sndbuf_feed(b, bs, c, c->feeder, amt) : 0; |
| 428 | ||
| 429 | amt = sndbuf_getready(b); | |
| 558a398b SS |
430 | if (amt > 0) { |
| 431 | c->xruns++; | |
| 432 | sndbuf_dispose(b, NULL, amt); | |
| 433 | } | |
| 984263bc MD |
434 | |
| 435 | chn_wakeup(c); | |
| 436 | ||
| 437 | return ret; | |
| 438 | } | |
| 439 | ||
| 440 | void | |
| 441 | chn_rdupdate(struct pcm_channel *c) | |
| 442 | { | |
| 443 | int ret; | |
| 444 | ||
| 445 | CHN_LOCKASSERT(c); | |
| 446 | KASSERT(c->direction == PCMDIR_REC, ("chn_rdupdate on bad channel")); | |
| 447 | ||
| 448 | if ((c->flags & CHN_F_MAPPED) || !(c->flags & CHN_F_TRIGGERED)) | |
| 449 | return; | |
| 450 | chn_trigger(c, PCMTRIG_EMLDMARD); | |
| 451 | chn_dmaupdate(c); | |
| 452 | ret = chn_rdfeed(c); | |
| 558a398b SS |
453 | DEB(if (ret) |
| 454 | kprintf("chn_rdfeed: %d\n", ret);) | |
| 984263bc MD |
455 | } |
| 456 | ||
| 457 | /* read interrupt routine. Must be called with interrupts blocked. */ | |
| 458 | static void | |
| 459 | chn_rdintr(struct pcm_channel *c) | |
| 460 | { | |
| 461 | int ret; | |
| 462 | ||
| 463 | CHN_LOCKASSERT(c); | |
| 464 | /* tell the driver to update the primary buffer if non-dma */ | |
| 465 | chn_trigger(c, PCMTRIG_EMLDMARD); | |
| 466 | /* update pointers in primary buffer */ | |
| 467 | chn_dmaupdate(c); | |
| 468 | /* ...and feed from primary to secondary */ | |
| 469 | ret = chn_rdfeed(c); | |
| 470 | } | |
| 471 | ||
| 472 | /* | |
| 473 | * user read routine - trigger if necessary, uiomove data from secondary buffer | |
| 474 | * if blocking, sleep, rinse and repeat. | |
| 475 | * | |
| 476 | * called externally, so must handle locking | |
| 477 | */ | |
| 478 | ||
| 479 | int | |
| 9ba76b73 | 480 | chn_read(struct pcm_channel *c, struct uio *buf, int ioflags) |
| 984263bc | 481 | { |
| 558a398b | 482 | int ret, timeout, sz, count; |
| 9ba76b73 | 483 | int nbio; |
| 984263bc | 484 | struct snd_dbuf *bs = c->bufsoft; |
| 558a398b SS |
485 | void *off; |
| 486 | int t, x,togo,p; | |
| 984263bc MD |
487 | |
| 488 | CHN_LOCKASSERT(c); | |
| 558a398b | 489 | nbio = (c->flags & CHN_F_NBIO) || (ioflags & IO_NDELAY); |
| 984263bc MD |
490 | if (!(c->flags & CHN_F_TRIGGERED)) |
| 491 | chn_start(c, 0); | |
| 492 | ||
| 493 | ret = 0; | |
| 494 | count = hz; | |
| 495 | while (!ret && (buf->uio_resid > 0) && (count > 0)) { | |
| e54488bb | 496 | sz = (int)szmin(buf->uio_resid, sndbuf_getready(bs)); |
| 984263bc MD |
497 | |
| 498 | if (sz > 0) { | |
| 558a398b SS |
499 | /* |
| 500 | * The following assumes that the free space in | |
| 501 | * the buffer can never be less around the | |
| 502 | * unlock-uiomove-lock sequence. | |
| 503 | */ | |
| 504 | togo = sz; | |
| 505 | while (ret == 0 && togo> 0) { | |
| 506 | p = sndbuf_getreadyptr(bs); | |
| 507 | t = MIN(togo, sndbuf_getsize(bs) - p); | |
| 508 | off = sndbuf_getbufofs(bs, p); | |
| 509 | CHN_UNLOCK(c); | |
| 510 | ret = uiomove(off, t, buf); | |
| 511 | CHN_LOCK(c); | |
| 512 | togo -= t; | |
| 513 | x = sndbuf_dispose(bs, NULL, t); | |
| 514 | } | |
| 515 | ret = 0; | |
| 984263bc | 516 | } else { |
| 9ba76b73 | 517 | if (nbio) { |
| 984263bc MD |
518 | ret = EWOULDBLOCK; |
| 519 | } else { | |
| 520 | timeout = (hz * sndbuf_getblksz(bs)) / (sndbuf_getspd(bs) * sndbuf_getbps(bs)); | |
| 521 | if (timeout < 1) | |
| 522 | timeout = 1; | |
| 523 | ret = chn_sleep(c, "pcmrd", timeout); | |
| 524 | if (ret == EWOULDBLOCK) { | |
| 525 | count -= timeout; | |
| 526 | ret = 0; | |
| 527 | } else { | |
| 528 | count = hz; | |
| 529 | } | |
| 530 | ||
| 531 | } | |
| 532 | } | |
| 533 | } | |
| 534 | ||
| 535 | if (count <= 0) { | |
| 536 | c->flags |= CHN_F_DEAD; | |
| e3869ec7 | 537 | kprintf("%s: record interrupt timeout, channel dead\n", c->name); |
| 984263bc MD |
538 | } |
| 539 | ||
| 540 | return ret; | |
| 541 | } | |
| 542 | ||
| 543 | void | |
| 544 | chn_intr(struct pcm_channel *c) | |
| 545 | { | |
| 546 | CHN_LOCK(c); | |
| 547 | c->interrupts++; | |
| 548 | if (c->direction == PCMDIR_PLAY) | |
| 549 | chn_wrintr(c); | |
| 550 | else | |
| 551 | chn_rdintr(c); | |
| 552 | CHN_UNLOCK(c); | |
| 553 | } | |
| 554 | ||
| 555 | u_int32_t | |
| 556 | chn_start(struct pcm_channel *c, int force) | |
| 557 | { | |
| 558 | u_int32_t i, j; | |
| 559 | struct snd_dbuf *b = c->bufhard; | |
| 560 | struct snd_dbuf *bs = c->bufsoft; | |
| 561 | ||
| 562 | CHN_LOCKASSERT(c); | |
| 563 | /* if we're running, or if we're prevented from triggering, bail */ | |
| 564 | if ((c->flags & CHN_F_TRIGGERED) || ((c->flags & CHN_F_NOTRIGGER) && !force)) | |
| 565 | return EINVAL; | |
| 566 | ||
| 567 | i = (c->direction == PCMDIR_PLAY)? sndbuf_getready(bs) : sndbuf_getfree(bs); | |
| 568 | j = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(b) : sndbuf_getready(b); | |
| 569 | if (force || (i >= j)) { | |
| 570 | c->flags |= CHN_F_TRIGGERED; | |
| 571 | /* | |
| 572 | * if we're starting because a vchan started, don't feed any data | |
| 573 | * or it becomes impossible to start vchans synchronised with the | |
| 574 | * first one. the hardbuf should be empty so we top it up with | |
| 575 | * silence to give it something to chew. the real data will be | |
| 576 | * fed at the first irq. | |
| 577 | */ | |
| 578 | if (c->direction == PCMDIR_PLAY) { | |
| 558a398b SS |
579 | /* |
| 580 | * Reduce pops during playback startup. | |
| 581 | */ | |
| 582 | sndbuf_fillsilence(b); | |
| 984263bc MD |
583 | if (SLIST_EMPTY(&c->children)) |
| 584 | chn_wrfeed(c); | |
| 984263bc MD |
585 | } |
| 586 | sndbuf_setrun(b, 1); | |
| 587 | c->xruns = 0; | |
| 588 | chn_trigger(c, PCMTRIG_START); | |
| 589 | return 0; | |
| 590 | } | |
| 591 | ||
| 592 | return 0; | |
| 593 | } | |
| 594 | ||
| 595 | void | |
| 596 | chn_resetbuf(struct pcm_channel *c) | |
| 597 | { | |
| 598 | struct snd_dbuf *b = c->bufhard; | |
| 599 | struct snd_dbuf *bs = c->bufsoft; | |
| 600 | ||
| 601 | c->blocks = 0; | |
| 602 | sndbuf_reset(b); | |
| 603 | sndbuf_reset(bs); | |
| 604 | } | |
| 605 | ||
| 606 | /* | |
| 607 | * chn_sync waits until the space in the given channel goes above | |
| 608 | * a threshold. The threshold is checked against fl or rl respectively. | |
| 609 | * Assume that the condition can become true, do not check here... | |
| 610 | */ | |
| 611 | int | |
| 612 | chn_sync(struct pcm_channel *c, int threshold) | |
| 613 | { | |
| 614 | u_long rdy; | |
| 615 | int ret; | |
| 616 | struct snd_dbuf *bs = c->bufsoft; | |
| 617 | ||
| 618 | CHN_LOCKASSERT(c); | |
| 558a398b SS |
619 | |
| 620 | /* if we haven't yet started and nothing is buffered, else start*/ | |
| 621 | if (!(c->flags & CHN_F_TRIGGERED)) { | |
| 622 | if (sndbuf_getready(bs) > 0) { | |
| 623 | ret = chn_start(c, 1); | |
| 624 | if (ret) | |
| 625 | return ret; | |
| 626 | } else { | |
| 627 | return 0; | |
| 628 | } | |
| 629 | } | |
| 630 | ||
| 631 | for (;;) { | |
| 984263bc MD |
632 | rdy = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(bs) : sndbuf_getready(bs); |
| 633 | if (rdy <= threshold) { | |
| 634 | ret = chn_sleep(c, "pcmsyn", 1); | |
| 635 | if (ret == ERESTART || ret == EINTR) { | |
| e3869ec7 | 636 | DEB(kprintf("chn_sync: tsleep returns %d\n", ret)); |
| 984263bc MD |
637 | return -1; |
| 638 | } | |
| 639 | } else | |
| 640 | break; | |
| 641 | } | |
| 642 | return 0; | |
| 643 | } | |
| 644 | ||
| 645 | /* called externally, handle locking */ | |
| 646 | int | |
| 558a398b | 647 | chn_poll(struct pcm_channel *c, int ev, struct thread *td) |
| 984263bc MD |
648 | { |
| 649 | struct snd_dbuf *bs = c->bufsoft; | |
| 650 | int ret; | |
| 651 | ||
| 652 | CHN_LOCKASSERT(c); | |
| 653 | if (!(c->flags & CHN_F_MAPPED) && !(c->flags & CHN_F_TRIGGERED)) | |
| 654 | chn_start(c, 1); | |
| 655 | ret = 0; | |
| 656 | if (chn_polltrigger(c) && chn_pollreset(c)) | |
| 657 | ret = ev; | |
| 658 | else | |
| 558a398b | 659 | selrecord(td, sndbuf_getsel(bs)); |
| 984263bc MD |
660 | return ret; |
| 661 | } | |
| 662 | ||
| 663 | /* | |
| 664 | * chn_abort terminates a running dma transfer. it may sleep up to 200ms. | |
| 665 | * it returns the number of bytes that have not been transferred. | |
| 666 | * | |
| 667 | * called from: dsp_close, dsp_ioctl, with channel locked | |
| 668 | */ | |
| 669 | int | |
| 670 | chn_abort(struct pcm_channel *c) | |
| 671 | { | |
| 672 | int missing = 0; | |
| 673 | struct snd_dbuf *b = c->bufhard; | |
| 674 | struct snd_dbuf *bs = c->bufsoft; | |
| 675 | ||
| 676 | CHN_LOCKASSERT(c); | |
| 677 | if (!(c->flags & CHN_F_TRIGGERED)) | |
| 678 | return 0; | |
| 679 | c->flags |= CHN_F_ABORTING; | |
| 680 | ||
| 681 | c->flags &= ~CHN_F_TRIGGERED; | |
| 682 | /* kill the channel */ | |
| 683 | chn_trigger(c, PCMTRIG_ABORT); | |
| 684 | sndbuf_setrun(b, 0); | |
| 685 | if (!(c->flags & CHN_F_VIRTUAL)) | |
| 686 | chn_dmaupdate(c); | |
| 687 | missing = sndbuf_getready(bs) + sndbuf_getready(b); | |
| 688 | ||
| 689 | c->flags &= ~CHN_F_ABORTING; | |
| 690 | return missing; | |
| 691 | } | |
| 692 | ||
| 693 | /* | |
| 694 | * this routine tries to flush the dma transfer. It is called | |
| 558a398b SS |
695 | * on a close of a playback channel. |
| 696 | * first, if there is data in the buffer, but the dma has not yet | |
| 697 | * begun, we need to start it. | |
| 698 | * next, we wait for the play buffer to drain | |
| 699 | * finally, we stop the dma. | |
| 984263bc | 700 | * |
| 558a398b | 701 | * called from: dsp_close, not valid for record channels. |
| 984263bc MD |
702 | */ |
| 703 | ||
| 704 | int | |
| 705 | chn_flush(struct pcm_channel *c) | |
| 706 | { | |
| 707 | int ret, count, resid, resid_p; | |
| 708 | struct snd_dbuf *b = c->bufhard; | |
| 709 | struct snd_dbuf *bs = c->bufsoft; | |
| 710 | ||
| 711 | CHN_LOCKASSERT(c); | |
| 558a398b SS |
712 | KASSERT(c->direction == PCMDIR_PLAY, ("chn_flush on bad channel")); |
| 713 | DEB(kprintf("chn_flush: c->flags 0x%08x\n", c->flags)); | |
| 714 | ||
| 715 | /* if we haven't yet started and nothing is buffered, else start*/ | |
| 716 | if (!(c->flags & CHN_F_TRIGGERED)) { | |
| 717 | if (sndbuf_getready(bs) > 0) { | |
| 718 | ret = chn_start(c, 1); | |
| 719 | if (ret) | |
| 720 | return ret; | |
| 721 | } else { | |
| 722 | return 0; | |
| 723 | } | |
| 724 | } | |
| 984263bc MD |
725 | |
| 726 | c->flags |= CHN_F_CLOSING; | |
| 727 | resid = sndbuf_getready(bs) + sndbuf_getready(b); | |
| 728 | resid_p = resid; | |
| 729 | count = 10; | |
| 730 | ret = 0; | |
| 731 | while ((count > 0) && (resid > sndbuf_getsize(b)) && (ret == 0)) { | |
| 732 | /* still pending output data. */ | |
| 733 | ret = chn_sleep(c, "pcmflu", hz / 10); | |
| 734 | if (ret == EWOULDBLOCK) | |
| 735 | ret = 0; | |
| 736 | if (ret == 0) { | |
| 737 | resid = sndbuf_getready(bs) + sndbuf_getready(b); | |
| 558a398b | 738 | if (resid == resid_p) |
| 984263bc | 739 | count--; |
| 558a398b SS |
740 | if (resid > resid_p) |
| 741 | DEB(printf("chn_flush: buffer length increasind %d -> %d\n", resid_p, resid)); | |
| 984263bc MD |
742 | resid_p = resid; |
| 743 | } | |
| 744 | } | |
| 745 | if (count == 0) | |
| 558a398b SS |
746 | DEB(kprintf("chn_flush: timeout, hw %d, sw %d\n", |
| 747 | sndbuf_getready(b), sndbuf_getready(bs))); | |
| 984263bc MD |
748 | |
| 749 | c->flags &= ~CHN_F_TRIGGERED; | |
| 750 | /* kill the channel */ | |
| 751 | chn_trigger(c, PCMTRIG_ABORT); | |
| 752 | sndbuf_setrun(b, 0); | |
| 753 | ||
| 754 | c->flags &= ~CHN_F_CLOSING; | |
| 755 | return 0; | |
| 756 | } | |
| 757 | ||
| 758 | int | |
| 759 | fmtvalid(u_int32_t fmt, u_int32_t *fmtlist) | |
| 760 | { | |
| 761 | int i; | |
| 762 | ||
| 763 | for (i = 0; fmtlist[i]; i++) | |
| 764 | if (fmt == fmtlist[i]) | |
| 765 | return 1; | |
| 766 | return 0; | |
| 767 | } | |
| 768 | ||
| 769 | int | |
| 770 | chn_reset(struct pcm_channel *c, u_int32_t fmt) | |
| 771 | { | |
| 772 | int hwspd, r; | |
| 773 | ||
| 774 | CHN_LOCKASSERT(c); | |
| 775 | c->flags &= CHN_F_RESET; | |
| 776 | c->interrupts = 0; | |
| 777 | c->xruns = 0; | |
| 778 | ||
| 779 | r = CHANNEL_RESET(c->methods, c->devinfo); | |
| 780 | if (fmt != 0) { | |
| 558a398b | 781 | #if 0 |
| 984263bc MD |
782 | hwspd = DSP_DEFAULT_SPEED; |
| 783 | /* only do this on a record channel until feederbuilder works */ | |
| 784 | if (c->direction == PCMDIR_REC) | |
| 785 | RANGE(hwspd, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed); | |
| 786 | c->speed = hwspd; | |
| 558a398b SS |
787 | #endif |
| 788 | hwspd = chn_getcaps(c)->minspeed; | |
| 789 | c->speed = hwspd; | |
| 984263bc MD |
790 | |
| 791 | if (r == 0) | |
| 792 | r = chn_setformat(c, fmt); | |
| 793 | if (r == 0) | |
| 794 | r = chn_setspeed(c, hwspd); | |
| 558a398b | 795 | #if 0 |
| 984263bc MD |
796 | if (r == 0) |
| 797 | r = chn_setvolume(c, 100, 100); | |
| 558a398b | 798 | #endif |
| 984263bc MD |
799 | } |
| 800 | if (r == 0) | |
| 801 | r = chn_setblocksize(c, 0, 0); | |
| 802 | if (r == 0) { | |
| 803 | chn_resetbuf(c); | |
| 804 | r = CHANNEL_RESETDONE(c->methods, c->devinfo); | |
| 805 | } | |
| 806 | return r; | |
| 807 | } | |
| 808 | ||
| 809 | int | |
| 558a398b | 810 | chn_init(struct pcm_channel *c, void *devinfo, int dir, int direction) |
| 984263bc MD |
811 | { |
| 812 | struct feeder_class *fc; | |
| 813 | struct snd_dbuf *b, *bs; | |
| 814 | int ret; | |
| 815 | ||
| 558a398b | 816 | chn_lockinit(c, dir); |
| 984263bc MD |
817 | |
| 818 | b = NULL; | |
| 819 | bs = NULL; | |
| 820 | c->devinfo = NULL; | |
| 821 | c->feeder = NULL; | |
| 822 | ||
| 558a398b SS |
823 | ret = ENOMEM; |
| 824 | b = sndbuf_create(c->dev, c->name, "primary", c); | |
| 825 | if (b == NULL) | |
| 826 | goto out; | |
| 827 | bs = sndbuf_create(c->dev, c->name, "secondary", c); | |
| 828 | if (bs == NULL) | |
| 829 | goto out; | |
| 830 | ||
| 831 | CHN_LOCK(c); | |
| 832 | ||
| 984263bc MD |
833 | ret = EINVAL; |
| 834 | fc = feeder_getclass(NULL); | |
| 835 | if (fc == NULL) | |
| 836 | goto out; | |
| 837 | if (chn_addfeeder(c, fc, NULL)) | |
| 838 | goto out; | |
| 839 | ||
| 558a398b SS |
840 | /* |
| 841 | * XXX - sndbuf_setup() & sndbuf_resize() expect to be called | |
| 842 | * with the channel unlocked because they are also called | |
| 843 | * from driver methods that don't know about locking | |
| 844 | */ | |
| 845 | CHN_UNLOCK(c); | |
| 984263bc | 846 | sndbuf_setup(bs, NULL, 0); |
| 558a398b | 847 | CHN_LOCK(c); |
| 984263bc MD |
848 | c->bufhard = b; |
| 849 | c->bufsoft = bs; | |
| 850 | c->flags = 0; | |
| 851 | c->feederflags = 0; | |
| 852 | ||
| 853 | ret = ENODEV; | |
| 558a398b SS |
854 | CHN_UNLOCK(c); /* XXX - Unlock for CHANNEL_INIT() kmalloc() call */ |
| 855 | c->devinfo = CHANNEL_INIT(c->methods, devinfo, b, c, direction); | |
| 856 | CHN_LOCK(c); | |
| 984263bc MD |
857 | if (c->devinfo == NULL) |
| 858 | goto out; | |
| 859 | ||
| 860 | ret = ENOMEM; | |
| 861 | if ((sndbuf_getsize(b) == 0) && ((c->flags & CHN_F_VIRTUAL) == 0)) | |
| 862 | goto out; | |
| 863 | ||
| 558a398b | 864 | ret = chn_setdir(c, direction); |
| 984263bc MD |
865 | if (ret) |
| 866 | goto out; | |
| 867 | ||
| 868 | ret = sndbuf_setfmt(b, AFMT_U8); | |
| 869 | if (ret) | |
| 870 | goto out; | |
| 871 | ||
| 872 | ret = sndbuf_setfmt(bs, AFMT_U8); | |
| 873 | if (ret) | |
| 874 | goto out; | |
| 875 | ||
| 34e3e97f SS |
876 | ret = chn_setvolume(c, 100, 100); |
| 877 | if (ret) | |
| 878 | goto out; | |
| 879 | ||
| 984263bc MD |
880 | |
| 881 | out: | |
| 558a398b | 882 | CHN_UNLOCK(c); |
| 984263bc MD |
883 | if (ret) { |
| 884 | if (c->devinfo) { | |
| 885 | if (CHANNEL_FREE(c->methods, c->devinfo)) | |
| 886 | sndbuf_free(b); | |
| 887 | } | |
| 888 | if (bs) | |
| 889 | sndbuf_destroy(bs); | |
| 890 | if (b) | |
| 891 | sndbuf_destroy(b); | |
| 892 | c->flags |= CHN_F_DEAD; | |
| 893 | chn_lockdestroy(c); | |
| 894 | ||
| 895 | return ret; | |
| 896 | } | |
| 897 | ||
| 984263bc MD |
898 | return 0; |
| 899 | } | |
| 900 | ||
| 901 | int | |
| 902 | chn_kill(struct pcm_channel *c) | |
| 903 | { | |
| 904 | struct snd_dbuf *b = c->bufhard; | |
| 905 | struct snd_dbuf *bs = c->bufsoft; | |
| 906 | ||
| 984263bc MD |
907 | if (c->flags & CHN_F_TRIGGERED) |
| 908 | chn_trigger(c, PCMTRIG_ABORT); | |
| 909 | while (chn_removefeeder(c) == 0); | |
| 910 | if (CHANNEL_FREE(c->methods, c->devinfo)) | |
| 911 | sndbuf_free(b); | |
| 912 | c->flags |= CHN_F_DEAD; | |
| 913 | sndbuf_destroy(bs); | |
| 914 | sndbuf_destroy(b); | |
| 915 | chn_lockdestroy(c); | |
| 916 | return 0; | |
| 917 | } | |
| 918 | ||
| 919 | int | |
| 920 | chn_setdir(struct pcm_channel *c, int dir) | |
| 921 | { | |
| 558a398b | 922 | #if NISA > 0 |
| 984263bc | 923 | struct snd_dbuf *b = c->bufhard; |
| 558a398b | 924 | #endif |
| 984263bc MD |
925 | int r; |
| 926 | ||
| 927 | CHN_LOCKASSERT(c); | |
| 928 | c->direction = dir; | |
| 929 | r = CHANNEL_SETDIR(c->methods, c->devinfo, c->direction); | |
| 558a398b SS |
930 | #if NISA > 0 |
| 931 | if (!r && SND_DMA(b)) | |
| 932 | sndbuf_dmasetdir(b, c->direction); | |
| 933 | #endif | |
| 984263bc MD |
934 | return r; |
| 935 | } | |
| 936 | ||
| 937 | int | |
| 938 | chn_setvolume(struct pcm_channel *c, int left, int right) | |
| 939 | { | |
| 940 | CHN_LOCKASSERT(c); | |
| 558a398b SS |
941 | /* should add a feeder for volume changing if channel returns -1 */ |
| 942 | if (left > 100) | |
| 943 | left = 100; | |
| 944 | if (left < 0) | |
| 945 | left = 0; | |
| 946 | if (right > 100) | |
| 947 | right = 100; | |
| 948 | if (right < 0) | |
| 949 | right = 0; | |
| 950 | c->volume = left | (right << 8); | |
| 984263bc MD |
951 | return 0; |
| 952 | } | |
| 953 | ||
| 954 | static int | |
| 955 | chn_tryspeed(struct pcm_channel *c, int speed) | |
| 956 | { | |
| 957 | struct pcm_feeder *f; | |
| 958 | struct snd_dbuf *b = c->bufhard; | |
| 959 | struct snd_dbuf *bs = c->bufsoft; | |
| 960 | struct snd_dbuf *x; | |
| 961 | int r, delta; | |
| 962 | ||
| 963 | CHN_LOCKASSERT(c); | |
| e3869ec7 SW |
964 | DEB(kprintf("setspeed, channel %s\n", c->name)); |
| 965 | DEB(kprintf("want speed %d, ", speed)); | |
| 984263bc MD |
966 | if (speed <= 0) |
| 967 | return EINVAL; | |
| 968 | if (CANCHANGE(c)) { | |
| 969 | r = 0; | |
| 970 | c->speed = speed; | |
| 971 | sndbuf_setspd(bs, speed); | |
| 972 | RANGE(speed, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed); | |
| e3869ec7 | 973 | DEB(kprintf("try speed %d, ", speed)); |
| 984263bc | 974 | sndbuf_setspd(b, CHANNEL_SETSPEED(c->methods, c->devinfo, speed)); |
| e3869ec7 | 975 | DEB(kprintf("got speed %d\n", sndbuf_getspd(b))); |
| 984263bc MD |
976 | |
| 977 | delta = sndbuf_getspd(b) - sndbuf_getspd(bs); | |
| 978 | if (delta < 0) | |
| 979 | delta = -delta; | |
| 980 | ||
| 981 | c->feederflags &= ~(1 << FEEDER_RATE); | |
| 558a398b SS |
982 | /* |
| 983 | * Used to be 500. It was too big! | |
| 984 | */ | |
| 985 | if (delta > 25) | |
| 984263bc MD |
986 | c->feederflags |= 1 << FEEDER_RATE; |
| 987 | else | |
| 988 | sndbuf_setspd(bs, sndbuf_getspd(b)); | |
| 989 | ||
| 990 | r = chn_buildfeeder(c); | |
| e3869ec7 | 991 | DEB(kprintf("r = %d\n", r)); |
| 984263bc MD |
992 | if (r) |
| 993 | goto out; | |
| 994 | ||
| 995 | r = chn_setblocksize(c, 0, 0); | |
| 996 | if (r) | |
| 997 | goto out; | |
| 998 | ||
| 999 | if (!(c->feederflags & (1 << FEEDER_RATE))) | |
| 1000 | goto out; | |
| 1001 | ||
| 1002 | r = EINVAL; | |
| 1003 | f = chn_findfeeder(c, FEEDER_RATE); | |
| e3869ec7 | 1004 | DEB(kprintf("feedrate = %p\n", f)); |
| 984263bc MD |
1005 | if (f == NULL) |
| 1006 | goto out; | |
| 1007 | ||
| 1008 | x = (c->direction == PCMDIR_REC)? b : bs; | |
| 1009 | r = FEEDER_SET(f, FEEDRATE_SRC, sndbuf_getspd(x)); | |
| e3869ec7 | 1010 | DEB(kprintf("feeder_set(FEEDRATE_SRC, %d) = %d\n", sndbuf_getspd(x), r)); |
| 984263bc MD |
1011 | if (r) |
| 1012 | goto out; | |
| 1013 | ||
| 1014 | x = (c->direction == PCMDIR_REC)? bs : b; | |
| 1015 | r = FEEDER_SET(f, FEEDRATE_DST, sndbuf_getspd(x)); | |
| e3869ec7 | 1016 | DEB(kprintf("feeder_set(FEEDRATE_DST, %d) = %d\n", sndbuf_getspd(x), r)); |
| 984263bc | 1017 | out: |
| 558a398b SS |
1018 | if (!r) |
| 1019 | r = CHANNEL_SETFORMAT(c->methods, c->devinfo, | |
| 1020 | sndbuf_getfmt(b)); | |
| 1021 | if (!r) | |
| 1022 | sndbuf_setfmt(bs, c->format); | |
| e3869ec7 | 1023 | DEB(kprintf("setspeed done, r = %d\n", r)); |
| 984263bc MD |
1024 | return r; |
| 1025 | } else | |
| 1026 | return EINVAL; | |
| 1027 | } | |
| 1028 | ||
| 1029 | int | |
| 1030 | chn_setspeed(struct pcm_channel *c, int speed) | |
| 1031 | { | |
| 1032 | int r, oldspeed = c->speed; | |
| 1033 | ||
| 1034 | r = chn_tryspeed(c, speed); | |
| 1035 | if (r) { | |
| e3869ec7 | 1036 | DEB(kprintf("Failed to set speed %d falling back to %d\n", speed, oldspeed)); |
| 984263bc MD |
1037 | r = chn_tryspeed(c, oldspeed); |
| 1038 | } | |
| 1039 | return r; | |
| 1040 | } | |
| 1041 | ||
| 1042 | static int | |
| 1043 | chn_tryformat(struct pcm_channel *c, u_int32_t fmt) | |
| 1044 | { | |
| 1045 | struct snd_dbuf *b = c->bufhard; | |
| 1046 | struct snd_dbuf *bs = c->bufsoft; | |
| 1047 | int r; | |
| 1048 | ||
| 1049 | CHN_LOCKASSERT(c); | |
| 1050 | if (CANCHANGE(c)) { | |
| e3869ec7 | 1051 | DEB(kprintf("want format %d\n", fmt)); |
| 984263bc MD |
1052 | c->format = fmt; |
| 1053 | r = chn_buildfeeder(c); | |
| 1054 | if (r == 0) { | |
| 1055 | sndbuf_setfmt(bs, c->format); | |
| 1056 | chn_resetbuf(c); | |
| 1057 | r = CHANNEL_SETFORMAT(c->methods, c->devinfo, sndbuf_getfmt(b)); | |
| 1058 | if (r == 0) | |
| 1059 | r = chn_tryspeed(c, c->speed); | |
| 1060 | } | |
| 1061 | return r; | |
| 1062 | } else | |
| 1063 | return EINVAL; | |
| 1064 | } | |
| 1065 | ||
| 1066 | int | |
| 1067 | chn_setformat(struct pcm_channel *c, u_int32_t fmt) | |
| 1068 | { | |
| 1069 | u_int32_t oldfmt = c->format; | |
| 1070 | int r; | |
| 1071 | ||
| 1072 | r = chn_tryformat(c, fmt); | |
| 1073 | if (r) { | |
| e3869ec7 | 1074 | DEB(kprintf("Format change %d failed, reverting to %d\n", fmt, oldfmt)); |
| 984263bc MD |
1075 | chn_tryformat(c, oldfmt); |
| 1076 | } | |
| 1077 | return r; | |
| 1078 | } | |
| 1079 | ||
| 558a398b SS |
1080 | /* |
| 1081 | * given a bufsz value, round it to a power of 2 in the min-max range | |
| 1082 | * XXX only works if min and max are powers of 2 | |
| 1083 | */ | |
| 1084 | static int | |
| 1085 | round_bufsz(int bufsz, int min, int max) | |
| 1086 | { | |
| 1087 | int tmp = min * 2; | |
| 1088 | ||
| 1089 | KASSERT((min & (min-1)) == 0, ("min %d must be power of 2\n", min)); | |
| 1090 | KASSERT((max & (max-1)) == 0, ("max %d must be power of 2\n", max)); | |
| 1091 | while (tmp <= bufsz) | |
| 1092 | tmp <<= 1; | |
| 1093 | tmp >>= 1; | |
| 1094 | if (tmp > max) | |
| 1095 | tmp = max; | |
| 1096 | return tmp; | |
| 1097 | } | |
| 1098 | ||
| 1099 | /* | |
| 1100 | * set the channel's blocksize both for soft and hard buffers. | |
| 1101 | * | |
| 1102 | * blksz should be a power of 2 between 2**4 and 2**16 -- it is useful | |
| 1103 | * that it has the same value for both bufsoft and bufhard. | |
| 1104 | * blksz == -1 computes values according to a target irq rate. | |
| 1105 | * blksz == 0 reuses previous values if available, otherwise | |
| 1106 | * behaves as for -1 | |
| 1107 | * | |
| 1108 | * blkcnt is set by the user, between 2 and (2**17)/blksz for bufsoft, | |
| 1109 | * but should be a power of 2 for bufhard to simplify life to low | |
| 1110 | * level drivers. | |
| 1111 | * Note, for the rec channel a large blkcnt is ok, | |
| 1112 | * but for the play channel we want blksz as small as possible to keep | |
| 1113 | * the delay small, because routines in the write path always try to | |
| 1114 | * keep bufhard full. | |
| 1115 | * | |
| 1116 | * Unless we have good reason to, use the values suggested by the caller. | |
| 1117 | */ | |
| 984263bc MD |
1118 | int |
| 1119 | chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz) | |
| 1120 | { | |
| 1121 | struct snd_dbuf *b = c->bufhard; | |
| 1122 | struct snd_dbuf *bs = c->bufsoft; | |
| 558a398b | 1123 | int irqhz, ret, maxsz, maxsize, reqblksz; |
| 984263bc MD |
1124 | |
| 1125 | CHN_LOCKASSERT(c); | |
| 558a398b SS |
1126 | if (!CANCHANGE(c) || (c->flags & CHN_F_MAPPED)) { |
| 1127 | KASSERT(sndbuf_getsize(bs) == 0 || | |
| 1128 | sndbuf_getsize(bs) >= sndbuf_getsize(b), | |
| 1129 | ("%s(%s): bufsoft size %d < bufhard size %d", __func__, | |
| 1130 | c->name, sndbuf_getsize(bs), sndbuf_getsize(b))); | |
| 984263bc | 1131 | return EINVAL; |
| 558a398b SS |
1132 | } |
| 1133 | c->flags |= CHN_F_SETBLOCKSIZE; | |
| 984263bc MD |
1134 | |
| 1135 | ret = 0; | |
| e3869ec7 | 1136 | DEB(kprintf("%s(%d, %d)\n", __func__, blkcnt, blksz)); |
| 558a398b SS |
1137 | if (blksz == 0 || blksz == -1) { /* let the driver choose values */ |
| 1138 | if (blksz == -1) /* delete previous values */ | |
| 984263bc | 1139 | c->flags &= ~CHN_F_HAS_SIZE; |
| 558a398b SS |
1140 | if (!(c->flags & CHN_F_HAS_SIZE)) { /* no previous value */ |
| 1141 | /* | |
| 1142 | * compute a base blksz according to the target irq | |
| 1143 | * rate, then round to a suitable power of 2 | |
| 1144 | * in the range 16.. 2^17/2. | |
| 1145 | * Finally compute a suitable blkcnt. | |
| 1146 | */ | |
| 1147 | blksz = round_bufsz( (sndbuf_getbps(bs) * | |
| 1148 | sndbuf_getspd(bs)) / chn_targetirqrate, | |
| 1149 | 16, CHN_2NDBUFMAXSIZE / 2); | |
| 984263bc | 1150 | blkcnt = CHN_2NDBUFMAXSIZE / blksz; |
| 558a398b | 1151 | } else { /* use previously defined value */ |
| 984263bc MD |
1152 | blkcnt = sndbuf_getblkcnt(bs); |
| 1153 | blksz = sndbuf_getblksz(bs); | |
| 984263bc MD |
1154 | } |
| 1155 | } else { | |
| 558a398b SS |
1156 | /* |
| 1157 | * use supplied values if reasonable. Note that here we | |
| 1158 | * might have blksz which is not a power of 2 if the | |
| 1159 | * ioctl() to compute it allows such values. | |
| 1160 | */ | |
| 984263bc MD |
1161 | ret = EINVAL; |
| 1162 | if ((blksz < 16) || (blkcnt < 2) || (blkcnt * blksz > CHN_2NDBUFMAXSIZE)) | |
| 1163 | goto out; | |
| 1164 | ret = 0; | |
| 1165 | c->flags |= CHN_F_HAS_SIZE; | |
| 1166 | } | |
| 1167 | ||
| 558a398b SS |
1168 | reqblksz = blksz; |
| 1169 | if (reqblksz < sndbuf_getbps(bs)) | |
| 1170 | reqblksz = sndbuf_getbps(bs); | |
| 1171 | if (reqblksz % sndbuf_getbps(bs)) | |
| 1172 | reqblksz -= reqblksz % sndbuf_getbps(bs); | |
| 984263bc MD |
1173 | |
| 1174 | /* adjust for different hw format/speed */ | |
| 558a398b SS |
1175 | /* |
| 1176 | * Now compute the approx irq rate for the given (soft) blksz, | |
| 1177 | * reduce to the acceptable range and compute a corresponding blksz | |
| 1178 | * for the hard buffer. Then set the channel's blocksize and | |
| 1179 | * corresponding hardbuf value. The number of blocks used should | |
| 1180 | * be set by the device-specific routine. In fact, even the | |
| 1181 | * call to sndbuf_setblksz() should not be here! XXX | |
| 1182 | */ | |
| 984263bc | 1183 | |
| 558a398b SS |
1184 | irqhz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / blksz; |
| 1185 | RANGE(irqhz, 16, 512); | |
| 984263bc | 1186 | |
| 558a398b SS |
1187 | maxsz = sndbuf_getmaxsize(b); |
| 1188 | if (maxsz == 0) /* virtual channels don't appear to allocate bufhard */ | |
| 1189 | maxsz = CHN_2NDBUFMAXSIZE; | |
| 1190 | blksz = round_bufsz( (sndbuf_getbps(b) * sndbuf_getspd(b)) / irqhz, | |
| 1191 | 16, maxsz / 2); | |
| 1192 | ||
| 1193 | /* Increase the size of bufsoft if before increasing bufhard. */ | |
| 1194 | maxsize = sndbuf_getsize(b); | |
| 1195 | if (sndbuf_getsize(bs) > maxsize) | |
| 1196 | maxsize = sndbuf_getsize(bs); | |
| 1197 | if (reqblksz * blkcnt > maxsize) | |
| 1198 | maxsize = reqblksz * blkcnt; | |
| 1199 | if (sndbuf_getsize(bs) != maxsize || sndbuf_getblksz(bs) != reqblksz) { | |
| 1200 | ret = sndbuf_remalloc(bs, maxsize/reqblksz, reqblksz); | |
| 1201 | if (ret) | |
| 1202 | goto out1; | |
| 1203 | } | |
| 984263bc | 1204 | |
| 558a398b | 1205 | CHN_UNLOCK(c); |
| 984263bc | 1206 | sndbuf_setblksz(b, CHANNEL_SETBLOCKSIZE(c->methods, c->devinfo, blksz)); |
| 558a398b | 1207 | CHN_LOCK(c); |
| 984263bc | 1208 | |
| 558a398b SS |
1209 | /* Decrease the size of bufsoft after decreasing bufhard. */ |
| 1210 | maxsize = sndbuf_getsize(b); | |
| 1211 | if (reqblksz * blkcnt > maxsize) | |
| 1212 | maxsize = reqblksz * blkcnt; | |
| 1213 | if (maxsize > sndbuf_getsize(bs)) | |
| 1214 | kprintf("Danger! %s bufsoft size increasing from %d to %d after CHANNEL_SETBLOCKSIZE()\n", | |
| 1215 | c->name, sndbuf_getsize(bs), maxsize); | |
| 1216 | if (sndbuf_getsize(bs) != maxsize || sndbuf_getblksz(bs) != reqblksz) { | |
| 1217 | ret = sndbuf_remalloc(bs, maxsize/reqblksz, reqblksz); | |
| 1218 | if (ret) | |
| 1219 | goto out1; | |
| 1220 | } | |
| 984263bc MD |
1221 | |
| 1222 | chn_resetbuf(c); | |
| 558a398b SS |
1223 | out1: |
| 1224 | KASSERT(sndbuf_getsize(bs) == 0 || | |
| 1225 | sndbuf_getsize(bs) >= sndbuf_getsize(b), | |
| 1226 | ("%s(%s): bufsoft size %d < bufhard size %d, reqblksz=%d blksz=%d maxsize=%d blkcnt=%d", | |
| 1227 | __func__, c->name, sndbuf_getsize(bs), sndbuf_getsize(b), reqblksz, | |
| 1228 | blksz, maxsize, blkcnt)); | |
| 984263bc | 1229 | out: |
| 558a398b SS |
1230 | c->flags &= ~CHN_F_SETBLOCKSIZE; |
| 1231 | #if 0 | |
| 1232 | if (1) { | |
| 1233 | static uint32_t kk = 0; | |
| 1234 | printf("%u: b %d/%d/%d : (%d)%d/0x%0x | bs %d/%d/%d : (%d)%d/0x%0x\n", ++kk, | |
| 1235 | sndbuf_getsize(b), sndbuf_getblksz(b), sndbuf_getblkcnt(b), | |
| 1236 | sndbuf_getbps(b), | |
| 1237 | sndbuf_getspd(b), sndbuf_getfmt(b), | |
| 1238 | sndbuf_getsize(bs), sndbuf_getblksz(bs), sndbuf_getblkcnt(bs), | |
| 1239 | sndbuf_getbps(bs), | |
| 1240 | sndbuf_getspd(bs), sndbuf_getfmt(bs)); | |
| 1241 | if (sndbuf_getsize(b) % sndbuf_getbps(b) || | |
| 1242 | sndbuf_getblksz(b) % sndbuf_getbps(b) || | |
| 1243 | sndbuf_getsize(bs) % sndbuf_getbps(bs) || | |
| 1244 | sndbuf_getblksz(b) % sndbuf_getbps(b)) { | |
| 1245 | printf("%u: bps/blksz alignment screwed!\n", kk); | |
| 1246 | } | |
| 1247 | } | |
| 1248 | #endif | |
| 984263bc MD |
1249 | return ret; |
| 1250 | } | |
| 1251 | ||
| 1252 | int | |
| 1253 | chn_trigger(struct pcm_channel *c, int go) | |
| 1254 | { | |
| 558a398b | 1255 | #if NISA > 0 |
| 984263bc | 1256 | struct snd_dbuf *b = c->bufhard; |
| 558a398b | 1257 | #endif |
| 984263bc MD |
1258 | int ret; |
| 1259 | ||
| 1260 | CHN_LOCKASSERT(c); | |
| 558a398b SS |
1261 | #if NISA > 0 |
| 1262 | if (SND_DMA(b) && (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)) | |
| 1263 | sndbuf_dmabounce(b); | |
| 1264 | #endif | |
| 984263bc MD |
1265 | ret = CHANNEL_TRIGGER(c->methods, c->devinfo, go); |
| 1266 | ||
| 1267 | return ret; | |
| 1268 | } | |
| 1269 | ||
| 1270 | int | |
| 1271 | chn_getptr(struct pcm_channel *c) | |
| 1272 | { | |
| 558a398b | 1273 | #if 0 |
| 984263bc MD |
1274 | int hwptr; |
| 1275 | int a = (1 << c->align) - 1; | |
| 1276 | ||
| 1277 | CHN_LOCKASSERT(c); | |
| 1278 | hwptr = (c->flags & CHN_F_TRIGGERED)? CHANNEL_GETPTR(c->methods, c->devinfo) : 0; | |
| 1279 | /* don't allow unaligned values in the hwa ptr */ | |
| 1280 | #if 1 | |
| 1281 | hwptr &= ~a ; /* Apply channel align mask */ | |
| 1282 | #endif | |
| 1283 | hwptr &= DMA_ALIGN_MASK; /* Apply DMA align mask */ | |
| 1284 | return hwptr; | |
| 558a398b SS |
1285 | #endif |
| 1286 | int hwptr; | |
| 1287 | ||
| 1288 | CHN_LOCKASSERT(c); | |
| 1289 | hwptr = (c->flags & CHN_F_TRIGGERED)? CHANNEL_GETPTR(c->methods, c->devinfo) : 0; | |
| 1290 | return (hwptr - (hwptr % sndbuf_getbps(c->bufhard))); | |
| 984263bc MD |
1291 | } |
| 1292 | ||
| 1293 | struct pcmchan_caps * | |
| 1294 | chn_getcaps(struct pcm_channel *c) | |
| 1295 | { | |
| 1296 | CHN_LOCKASSERT(c); | |
| 1297 | return CHANNEL_GETCAPS(c->methods, c->devinfo); | |
| 1298 | } | |
| 1299 | ||
| 1300 | u_int32_t | |
| 1301 | chn_getformats(struct pcm_channel *c) | |
| 1302 | { | |
| 1303 | u_int32_t *fmtlist, fmts; | |
| 1304 | int i; | |
| 1305 | ||
| 1306 | fmtlist = chn_getcaps(c)->fmtlist; | |
| 1307 | fmts = 0; | |
| 1308 | for (i = 0; fmtlist[i]; i++) | |
| 1309 | fmts |= fmtlist[i]; | |
| 1310 | ||
| 1311 | /* report software-supported formats */ | |
| 1312 | if (report_soft_formats) | |
| 558a398b SS |
1313 | fmts |= AFMT_MU_LAW|AFMT_A_LAW|AFMT_U32_LE|AFMT_U32_BE| |
| 1314 | AFMT_S32_LE|AFMT_S32_BE|AFMT_U24_LE|AFMT_U24_BE| | |
| 1315 | AFMT_S24_LE|AFMT_S24_BE|AFMT_U16_LE|AFMT_U16_BE| | |
| 984263bc MD |
1316 | AFMT_S16_LE|AFMT_S16_BE|AFMT_U8|AFMT_S8; |
| 1317 | ||
| 1318 | return fmts; | |
| 1319 | } | |
| 1320 | ||
| 1321 | static int | |
| 1322 | chn_buildfeeder(struct pcm_channel *c) | |
| 1323 | { | |
| 1324 | struct feeder_class *fc; | |
| 1325 | struct pcm_feederdesc desc; | |
| 558a398b | 1326 | u_int32_t tmp[2], type, flags, hwfmt, *fmtlist; |
| 984263bc MD |
1327 | int err; |
| 1328 | ||
| 1329 | CHN_LOCKASSERT(c); | |
| 1330 | while (chn_removefeeder(c) == 0); | |
| 1331 | KASSERT((c->feeder == NULL), ("feeder chain not empty")); | |
| 1332 | ||
| 1333 | c->align = sndbuf_getalign(c->bufsoft); | |
| 1334 | ||
| 1335 | if (SLIST_EMPTY(&c->children)) { | |
| 1336 | fc = feeder_getclass(NULL); | |
| 1337 | KASSERT(fc != NULL, ("can't find root feeder")); | |
| 1338 | ||
| 1339 | err = chn_addfeeder(c, fc, NULL); | |
| 1340 | if (err) { | |
| e3869ec7 | 1341 | DEB(kprintf("can't add root feeder, err %d\n", err)); |
| 984263bc MD |
1342 | |
| 1343 | return err; | |
| 1344 | } | |
| 1345 | c->feeder->desc->out = c->format; | |
| 1346 | } else { | |
| 558a398b SS |
1347 | if (c->flags & CHN_F_HAS_VCHAN) { |
| 1348 | desc.type = FEEDER_MIXER; | |
| 1349 | desc.in = 0; | |
| 1350 | } else { | |
| 1351 | DEB(printf("can't decide which feeder type to use!\n")); | |
| 1352 | return EOPNOTSUPP; | |
| 1353 | } | |
| 984263bc MD |
1354 | desc.out = c->format; |
| 1355 | desc.flags = 0; | |
| 1356 | fc = feeder_getclass(&desc); | |
| 1357 | if (fc == NULL) { | |
| e3869ec7 | 1358 | DEB(kprintf("can't find vchan feeder\n")); |
| 984263bc MD |
1359 | |
| 1360 | return EOPNOTSUPP; | |
| 1361 | } | |
| 1362 | ||
| 1363 | err = chn_addfeeder(c, fc, &desc); | |
| 1364 | if (err) { | |
| e3869ec7 | 1365 | DEB(kprintf("can't add vchan feeder, err %d\n", err)); |
| 984263bc MD |
1366 | |
| 1367 | return err; | |
| 1368 | } | |
| 1369 | } | |
| 558a398b SS |
1370 | c->feederflags &= ~(1 << FEEDER_VOLUME); |
| 1371 | if (c->direction == PCMDIR_PLAY && | |
| 1372 | !(c->flags & CHN_F_VIRTUAL) && | |
| 4886ec58 | 1373 | c->parentsnddev && (c->parentsnddev->flags & SD_F_SOFTPCMVOL) && |
| 558a398b SS |
1374 | c->parentsnddev->mixer_dev) |
| 1375 | c->feederflags |= 1 << FEEDER_VOLUME; | |
| 984263bc | 1376 | flags = c->feederflags; |
| 558a398b | 1377 | fmtlist = chn_getcaps(c)->fmtlist; |
| 984263bc | 1378 | |
| 558a398b | 1379 | DEB(kprintf("feederflags %x\n", flags)); |
| 984263bc MD |
1380 | |
| 1381 | for (type = FEEDER_RATE; type <= FEEDER_LAST; type++) { | |
| 1382 | if (flags & (1 << type)) { | |
| 1383 | desc.type = type; | |
| 1384 | desc.in = 0; | |
| 1385 | desc.out = 0; | |
| 1386 | desc.flags = 0; | |
| e3869ec7 | 1387 | DEB(kprintf("find feeder type %d, ", type)); |
| 984263bc | 1388 | fc = feeder_getclass(&desc); |
| e3869ec7 | 1389 | DEB(kprintf("got %p\n", fc)); |
| 984263bc | 1390 | if (fc == NULL) { |
| e3869ec7 | 1391 | DEB(kprintf("can't find required feeder type %d\n", type)); |
| 984263bc MD |
1392 | |
| 1393 | return EOPNOTSUPP; | |
| 1394 | } | |
| 1395 | ||
| 558a398b SS |
1396 | DEB(kprintf("build fmtchain from 0x%08x to 0x%08x: ", c->feeder->desc->out, fc->desc->in)); |
| 1397 | tmp[0] = fc->desc->in; | |
| 1398 | tmp[1] = 0; | |
| 1399 | if (chn_fmtchain(c, tmp) == 0) { | |
| 1400 | DEB(printf("failed\n")); | |
| 984263bc | 1401 | |
| 558a398b | 1402 | return ENODEV; |
| 984263bc | 1403 | } |
| 558a398b | 1404 | DEB(printf("ok\n")); |
| 984263bc MD |
1405 | |
| 1406 | err = chn_addfeeder(c, fc, fc->desc); | |
| 1407 | if (err) { | |
| 558a398b | 1408 | DEB(kprintf("can't add feeder %p, output 0x%x, err %d\n", fc, fc->desc->out, err)); |
| 984263bc MD |
1409 | |
| 1410 | return err; | |
| 1411 | } | |
| 558a398b | 1412 | DEB(kprintf("added feeder %p, output 0x%x\n", fc, c->feeder->desc->out)); |
| 984263bc MD |
1413 | } |
| 1414 | } | |
| 1415 | ||
| 558a398b SS |
1416 | if (c->direction == PCMDIR_REC) { |
| 1417 | tmp[0] = c->format; | |
| 1418 | tmp[1] = 0; | |
| 1419 | hwfmt = chn_fmtchain(c, tmp); | |
| 1420 | } else | |
| 1421 | hwfmt = chn_fmtchain(c, fmtlist); | |
| 984263bc | 1422 | |
| 558a398b SS |
1423 | if (hwfmt == 0 || !fmtvalid(hwfmt, fmtlist)) { |
| 1424 | DEB(printf("Invalid hardware format: 0x%08x\n", hwfmt)); | |
| 984263bc | 1425 | return ENODEV; |
| 558a398b | 1426 | } |
| 984263bc MD |
1427 | |
| 1428 | sndbuf_setfmt(c->bufhard, hwfmt); | |
| 1429 | ||
| 558a398b SS |
1430 | if ((flags & (1 << FEEDER_VOLUME))) { |
| 1431 | struct dev_ioctl_args map; | |
| 4886ec58 HT |
1432 | u_int32_t parent = SOUND_MIXER_NONE; |
| 1433 | int vol, left, right; | |
| 1434 | ||
| 1435 | vol = 100 | (100 << 8); | |
| 558a398b SS |
1436 | |
| 1437 | CHN_UNLOCK(c); | |
| 1438 | /* | |
| 1439 | * XXX This is ugly! The way mixer subs being so secretive | |
| 1440 | * about its own internals force us to use this silly | |
| 1441 | * monkey trick. | |
| 1442 | */ | |
| 1443 | map.a_head.a_dev = c->parentsnddev->mixer_dev; | |
| 1444 | map.a_cmd = MIXER_READ(SOUND_MIXER_PCM); | |
| 1445 | map.a_data = (caddr_t)&vol; | |
| 1446 | map.a_fflag = -1; | |
| 1447 | map.a_cred = NULL; | |
| 1448 | if (mixer_ioctl(&map) != 0) | |
| 4886ec58 HT |
1449 | device_printf(c->dev, "Soft PCM Volume: Failed to read default value\n"); |
| 1450 | left = vol & 0x7f; | |
| 1451 | right = (vol >> 8) & 0x7f; | |
| 1452 | if (c->parentsnddev != NULL && | |
| 1453 | c->parentsnddev->mixer_dev != NULL && | |
| 1454 | c->parentsnddev->mixer_dev->si_drv1 != NULL) | |
| 1455 | parent = mix_getparent( | |
| 1456 | c->parentsnddev->mixer_dev->si_drv1, | |
| 1457 | SOUND_MIXER_PCM); | |
| 1458 | if (parent != SOUND_MIXER_NONE) { | |
| 1459 | vol = 100 | (100 << 8); | |
| 1460 | map.a_head.a_dev = c->parentsnddev->mixer_dev; | |
| 1461 | map.a_cmd = MIXER_READ(parent); | |
| 1462 | map.a_data = (caddr_t)&vol; | |
| 1463 | map.a_fflag = -1; | |
| 1464 | map.a_cred = NULL; | |
| 1465 | if (mixer_ioctl(&map) != 0) | |
| 1466 | device_printf(c->dev, "Soft Volume: Failed to read parent default value\n"); | |
| 1467 | left = (left * (vol & 0x7f)) / 100; | |
| 1468 | right = (right * ((vol >> 8) & 0x7f)) / 100; | |
| 1469 | } | |
| 1470 | ||
| 558a398b | 1471 | CHN_LOCK(c); |
| 4886ec58 | 1472 | chn_setvolume(c, left, right); |
| 558a398b SS |
1473 | } |
| 1474 | ||
| 984263bc MD |
1475 | return 0; |
| 1476 | } | |
| 1477 | ||
| 1478 | int | |
| 1479 | chn_notify(struct pcm_channel *c, u_int32_t flags) | |
| 1480 | { | |
| 1481 | struct pcmchan_children *pce; | |
| 1482 | struct pcm_channel *child; | |
| 1483 | int run; | |
| 1484 | ||
| 558a398b SS |
1485 | CHN_LOCK(c); |
| 1486 | ||
| 1487 | if (SLIST_EMPTY(&c->children)) { | |
| 1488 | CHN_UNLOCK(c); | |
| 984263bc | 1489 | return ENODEV; |
| 558a398b | 1490 | } |
| 984263bc MD |
1491 | |
| 1492 | run = (c->flags & CHN_F_TRIGGERED)? 1 : 0; | |
| 1493 | /* | |
| 1494 | * if the hwchan is running, we can't change its rate, format or | |
| 1495 | * blocksize | |
| 1496 | */ | |
| 1497 | if (run) | |
| 1498 | flags &= CHN_N_VOLUME | CHN_N_TRIGGER; | |
| 1499 | ||
| 1500 | if (flags & CHN_N_RATE) { | |
| 1501 | /* | |
| 1502 | * we could do something here, like scan children and decide on | |
| 1503 | * the most appropriate rate to mix at, but we don't for now | |
| 1504 | */ | |
| 1505 | } | |
| 1506 | if (flags & CHN_N_FORMAT) { | |
| 1507 | /* | |
| 1508 | * we could do something here, like scan children and decide on | |
| 1509 | * the most appropriate mixer feeder to use, but we don't for now | |
| 1510 | */ | |
| 1511 | } | |
| 1512 | if (flags & CHN_N_VOLUME) { | |
| 1513 | /* | |
| 1514 | * we could do something here but we don't for now | |
| 1515 | */ | |
| 1516 | } | |
| 1517 | if (flags & CHN_N_BLOCKSIZE) { | |
| 1518 | int blksz; | |
| 1519 | /* | |
| 1520 | * scan the children, find the lowest blocksize and use that | |
| 1521 | * for the hard blocksize | |
| 1522 | */ | |
| 1523 | blksz = sndbuf_getmaxsize(c->bufhard) / 2; | |
| 1524 | SLIST_FOREACH(pce, &c->children, link) { | |
| 1525 | child = pce->channel; | |
| 558a398b | 1526 | CHN_LOCK(child); |
| 984263bc MD |
1527 | if (sndbuf_getblksz(child->bufhard) < blksz) |
| 1528 | blksz = sndbuf_getblksz(child->bufhard); | |
| 558a398b | 1529 | CHN_UNLOCK(child); |
| 984263bc MD |
1530 | } |
| 1531 | chn_setblocksize(c, 2, blksz); | |
| 1532 | } | |
| 1533 | if (flags & CHN_N_TRIGGER) { | |
| 1534 | int nrun; | |
| 1535 | /* | |
| 1536 | * scan the children, and figure out if any are running | |
| 1537 | * if so, we need to be running, otherwise we need to be stopped | |
| 1538 | * if we aren't in our target sstate, move to it | |
| 1539 | */ | |
| 1540 | nrun = 0; | |
| 1541 | SLIST_FOREACH(pce, &c->children, link) { | |
| 1542 | child = pce->channel; | |
| 558a398b | 1543 | CHN_LOCK(child); |
| 984263bc MD |
1544 | if (child->flags & CHN_F_TRIGGERED) |
| 1545 | nrun = 1; | |
| 558a398b | 1546 | CHN_UNLOCK(child); |
| 984263bc MD |
1547 | } |
| 1548 | if (nrun && !run) | |
| 1549 | chn_start(c, 1); | |
| 1550 | if (!nrun && run) | |
| 1551 | chn_abort(c); | |
| 1552 | } | |
| 558a398b | 1553 | CHN_UNLOCK(c); |
| 984263bc MD |
1554 | return 0; |
| 1555 | } | |
| 558a398b SS |
1556 | |
| 1557 | void | |
| 1558 | chn_lock(struct pcm_channel *c) | |
| 1559 | { | |
| 1560 | CHN_LOCK(c); | |
| 1561 | } | |
| 1562 | ||
| 1563 | void | |
| 1564 | chn_unlock(struct pcm_channel *c) | |
| 1565 | { | |
| 1566 | CHN_UNLOCK(c); | |
| 1567 | } |