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