1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org> 5 * Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006 6 * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org> 7 * All rights reserved. 8 * Copyright (c) 2025 The FreeBSD Foundation 9 * 10 * Portions of this software were developed by Christos Margiolis 11 * <christos@FreeBSD.org> under sponsorship from the FreeBSD Foundation. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #ifdef HAVE_KERNEL_OPTION_HEADERS 36 #include "opt_snd.h" 37 #endif 38 39 #include <sys/refcount.h> 40 #include <dev/sound/pcm/sound.h> 41 42 #include "feeder_if.h" 43 44 #define SND_USE_FXDIV 45 #define SND_DECLARE_FXDIV 46 #include "snd_fxdiv_gen.h" 47 48 struct snd_dbuf * 49 sndbuf_create(struct pcm_channel *channel, uint32_t fmt, uint32_t spd, 50 const char *desc) 51 { 52 struct snd_dbuf *b; 53 54 b = malloc(sizeof(*b), M_DEVBUF, M_WAITOK | M_ZERO); 55 refcount_init(&b->refcount, 1); 56 snprintf(b->name, SNDBUF_NAMELEN, "%s:%s", channel->name, desc); 57 b->channel = channel; 58 sndbuf_setfmt(b, fmt); 59 sndbuf_setspd(b, spd); 60 61 return b; 62 } 63 64 void 65 sndbuf_destroy(struct snd_dbuf *b) 66 { 67 b->flags |= SNDBUF_F_DETACHED; 68 sndbuf_rele(b); 69 } 70 71 void 72 sndbuf_ref(struct snd_dbuf *b) 73 { 74 unsigned int count __diagused; 75 76 CHN_LOCK(b->channel); 77 count = refcount_acquire(&b->refcount); 78 KASSERT(count > 0, ("sndbuf %p refcount 0", b)); 79 CHN_UNLOCK(b->channel); 80 } 81 82 void 83 sndbuf_rele(struct snd_dbuf *b) 84 { 85 if (refcount_release(&b->refcount)) { 86 sndbuf_free(b); 87 KASSERT(refcount_load(&b->refcount) == 0, 88 ("sndbuf %p still referenced", b)); 89 free(b, M_DEVBUF); 90 } 91 } 92 93 static void 94 sndbuf_setmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) 95 { 96 struct snd_dbuf *b = (struct snd_dbuf *)arg; 97 98 if (snd_verbose > 3) { 99 printf("sndbuf_setmap %lx, %lx; ", 100 (unsigned long)segs[0].ds_addr, 101 (unsigned long)segs[0].ds_len); 102 printf("%p -> %lx\n", b->buf, (unsigned long)segs[0].ds_addr); 103 } 104 if (error == 0) 105 b->buf_addr = segs[0].ds_addr; 106 else 107 b->buf_addr = 0; 108 } 109 110 /* 111 * Allocate memory for DMA buffer. If the device does not use DMA transfers, 112 * the driver can call malloc(9) and sndbuf_setup() itself. 113 */ 114 115 int 116 sndbuf_alloc(struct snd_dbuf *b, bus_dma_tag_t dmatag, int dmaflags, 117 unsigned int size) 118 { 119 int ret; 120 121 b->dmatag = dmatag; 122 b->dmaflags = dmaflags | BUS_DMA_NOWAIT | BUS_DMA_COHERENT; 123 b->maxsize = size; 124 b->bufsize = b->maxsize; 125 b->buf_addr = 0; 126 b->flags |= SNDBUF_F_MANAGED; 127 if (bus_dmamem_alloc(b->dmatag, (void **)&b->buf, b->dmaflags, 128 &b->dmamap)) { 129 sndbuf_free(b); 130 return (ENOMEM); 131 } 132 if (bus_dmamap_load(b->dmatag, b->dmamap, b->buf, b->maxsize, 133 sndbuf_setmap, b, BUS_DMA_NOWAIT) != 0 || b->buf_addr == 0) { 134 sndbuf_free(b); 135 return (ENOMEM); 136 } 137 138 ret = sndbuf_resize(b, 2, b->maxsize / 2); 139 if (ret != 0) 140 sndbuf_free(b); 141 142 return (ret); 143 } 144 145 int 146 sndbuf_setup(struct snd_dbuf *b, void *buf, unsigned int size) 147 { 148 b->flags &= ~SNDBUF_F_MANAGED; 149 if (buf) 150 b->flags |= SNDBUF_F_MANAGED; 151 b->buf = buf; 152 b->maxsize = size; 153 b->bufsize = b->maxsize; 154 return sndbuf_resize(b, 2, b->maxsize / 2); 155 } 156 157 void 158 sndbuf_free(struct snd_dbuf *b) 159 { 160 free(b->tmpbuf, M_DEVBUF); 161 free(b->shadbuf, M_DEVBUF); 162 163 if (b->buf) { 164 if (b->flags & SNDBUF_F_MANAGED) { 165 if (b->buf_addr) 166 bus_dmamap_unload(b->dmatag, b->dmamap); 167 if (b->dmatag) 168 bus_dmamem_free(b->dmatag, b->buf, b->dmamap); 169 } else 170 free(b->buf, M_DEVBUF); 171 } 172 seldrain(&b->sel); 173 174 b->tmpbuf = NULL; 175 b->shadbuf = NULL; 176 b->buf = NULL; 177 b->sl = 0; 178 b->dmatag = NULL; 179 b->dmamap = NULL; 180 } 181 182 #define SNDBUF_CACHE_SHIFT 5 183 184 int 185 sndbuf_resize(struct snd_dbuf *b, unsigned int blkcnt, unsigned int blksz) 186 { 187 unsigned int bufsize, allocsize; 188 uint8_t *tmpbuf; 189 190 CHN_LOCK(b->channel); 191 if (b->maxsize == 0) 192 goto out; 193 if (blkcnt == 0) 194 blkcnt = b->blkcnt; 195 if (blksz == 0) 196 blksz = b->blksz; 197 if (blkcnt < 2 || blksz < 16 || (blkcnt * blksz) > b->maxsize) { 198 CHN_UNLOCK(b->channel); 199 return EINVAL; 200 } 201 if (blkcnt == b->blkcnt && blksz == b->blksz) 202 goto out; 203 204 bufsize = blkcnt * blksz; 205 206 if (bufsize > b->allocsize || 207 bufsize < (b->allocsize >> SNDBUF_CACHE_SHIFT)) { 208 if (refcount_load(&b->refcount) > 1 || 209 (b->flags & SNDBUF_F_DETACHED) != 0) { 210 CHN_UNLOCK(b->channel); 211 return (EBUSY); 212 } 213 allocsize = round_page(bufsize); 214 CHN_UNLOCK(b->channel); 215 tmpbuf = malloc(allocsize, M_DEVBUF, M_WAITOK); 216 CHN_LOCK(b->channel); 217 if (snd_verbose > 3) 218 printf("%s(): b=%p %p -> %p [%d -> %d : %d]\n", 219 __func__, b, b->tmpbuf, tmpbuf, 220 b->allocsize, allocsize, bufsize); 221 free(b->tmpbuf, M_DEVBUF); 222 b->tmpbuf = tmpbuf; 223 b->allocsize = allocsize; 224 } else if (snd_verbose > 3) 225 printf("%s(): b=%p %d [%d] NOCHANGE\n", 226 __func__, b, b->allocsize, b->bufsize); 227 228 b->blkcnt = blkcnt; 229 b->blksz = blksz; 230 b->bufsize = bufsize; 231 232 sndbuf_reset(b); 233 out: 234 CHN_UNLOCK(b->channel); 235 return 0; 236 } 237 238 int 239 sndbuf_remalloc(struct snd_dbuf *b, unsigned int blkcnt, unsigned int blksz) 240 { 241 unsigned int bufsize, allocsize; 242 uint8_t *buf, *tmpbuf, *shadbuf; 243 244 if (blkcnt < 2 || blksz < 16) 245 return EINVAL; 246 247 CHN_LOCKASSERT(b->channel); 248 249 bufsize = blksz * blkcnt; 250 251 if (bufsize > b->allocsize || 252 bufsize < (b->allocsize >> SNDBUF_CACHE_SHIFT)) { 253 if (refcount_load(&b->refcount) > 1 || 254 (b->flags & SNDBUF_F_DETACHED) != 0) 255 return (EBUSY); 256 allocsize = round_page(bufsize); 257 CHN_UNLOCK(b->channel); 258 buf = malloc(allocsize, M_DEVBUF, M_WAITOK); 259 tmpbuf = malloc(allocsize, M_DEVBUF, M_WAITOK); 260 shadbuf = malloc(allocsize, M_DEVBUF, M_WAITOK); 261 CHN_LOCK(b->channel); 262 free(b->buf, M_DEVBUF); 263 b->buf = buf; 264 free(b->tmpbuf, M_DEVBUF); 265 b->tmpbuf = tmpbuf; 266 free(b->shadbuf, M_DEVBUF); 267 b->shadbuf = shadbuf; 268 if (snd_verbose > 3) 269 printf("%s(): b=%p %d -> %d [%d]\n", 270 __func__, b, b->allocsize, allocsize, bufsize); 271 b->allocsize = allocsize; 272 } else if (snd_verbose > 3) 273 printf("%s(): b=%p %d [%d] NOCHANGE\n", 274 __func__, b, b->allocsize, b->bufsize); 275 276 b->blkcnt = blkcnt; 277 b->blksz = blksz; 278 b->bufsize = bufsize; 279 b->maxsize = bufsize; 280 b->sl = bufsize; 281 282 sndbuf_reset(b); 283 284 return 0; 285 } 286 287 /** 288 * @brief Zero out space in buffer free area 289 * 290 * This function clears a chunk of @c length bytes in the buffer free area 291 * (i.e., where the next write will be placed). 292 * 293 * @param b buffer context 294 * @param length number of bytes to blank 295 */ 296 void 297 sndbuf_clear(struct snd_dbuf *b, unsigned int length) 298 { 299 int i; 300 u_char data, *p; 301 302 if (length == 0) 303 return; 304 if (length > b->bufsize) 305 length = b->bufsize; 306 307 data = sndbuf_zerodata(b->fmt); 308 i = sndbuf_getfreeptr(b); 309 p = b->buf; 310 for (; length > 0; length--, i++) 311 p[i % b->bufsize] = data; 312 } 313 314 /** 315 * @brief Zap buffer contents, resetting "ready area" fields 316 * 317 * @param b buffer context 318 */ 319 void 320 sndbuf_fillsilence(struct snd_dbuf *b) 321 { 322 if (b->bufsize > 0) 323 memset(b->buf, sndbuf_zerodata(b->fmt), b->bufsize); 324 b->rp = 0; 325 b->rl = b->bufsize; 326 } 327 328 void 329 sndbuf_fillsilence_rl(struct snd_dbuf *b, unsigned int rl) 330 { 331 if (b->bufsize > 0) 332 memset(b->buf, sndbuf_zerodata(b->fmt), b->bufsize); 333 b->rp = 0; 334 b->rl = min(b->bufsize, rl); 335 } 336 337 /** 338 * @brief Reset buffer w/o flushing statistics 339 * 340 * This function just zeroes out buffer contents and sets the "ready length" 341 * to zero. This was originally to facilitate minimal playback interruption 342 * (i.e., dropped samples) in SNDCTL_DSP_SILENCE/SKIP ioctls. 343 * 344 * @param b buffer context 345 */ 346 void 347 sndbuf_softreset(struct snd_dbuf *b) 348 { 349 b->rl = 0; 350 if (b->buf && b->bufsize > 0) 351 sndbuf_clear(b, b->bufsize); 352 } 353 354 void 355 sndbuf_reset(struct snd_dbuf *b) 356 { 357 b->hp = 0; 358 b->rp = 0; 359 b->rl = 0; 360 b->dl = 0; 361 b->prev_total = 0; 362 b->total = 0; 363 b->xrun = 0; 364 if (b->buf && b->bufsize > 0) 365 sndbuf_clear(b, b->bufsize); 366 sndbuf_clearshadow(b); 367 } 368 369 int 370 sndbuf_setfmt(struct snd_dbuf *b, uint32_t fmt) 371 { 372 b->fmt = fmt; 373 b->bps = AFMT_BPS(b->fmt); 374 b->align = AFMT_ALIGN(b->fmt); 375 return 0; 376 } 377 378 void 379 sndbuf_setspd(struct snd_dbuf *b, unsigned int spd) 380 { 381 b->spd = spd; 382 } 383 384 void * 385 sndbuf_getbufofs(struct snd_dbuf *b, unsigned int ofs) 386 { 387 KASSERT(ofs < b->bufsize, ("%s: ofs invalid %d", __func__, ofs)); 388 389 return b->buf + ofs; 390 } 391 392 unsigned int 393 sndbuf_runsz(struct snd_dbuf *b) 394 { 395 return b->dl; 396 } 397 398 void 399 sndbuf_setrun(struct snd_dbuf *b, int go) 400 { 401 b->dl = go? b->blksz : 0; 402 } 403 404 void 405 sndbuf_setxrun(struct snd_dbuf *b, unsigned int xrun) 406 { 407 b->xrun = xrun; 408 } 409 410 unsigned int 411 sndbuf_getready(struct snd_dbuf *b) 412 { 413 KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d", __func__, b->rl)); 414 415 return b->rl; 416 } 417 418 unsigned int 419 sndbuf_getreadyptr(struct snd_dbuf *b) 420 { 421 KASSERT((b->rp >= 0) && (b->rp <= b->bufsize), ("%s: b->rp invalid %d", __func__, b->rp)); 422 423 return b->rp; 424 } 425 426 unsigned int 427 sndbuf_getfree(struct snd_dbuf *b) 428 { 429 KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d", __func__, b->rl)); 430 431 return b->bufsize - b->rl; 432 } 433 434 unsigned int 435 sndbuf_getfreeptr(struct snd_dbuf *b) 436 { 437 KASSERT((b->rp >= 0) && (b->rp <= b->bufsize), ("%s: b->rp invalid %d", __func__, b->rp)); 438 KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d", __func__, b->rl)); 439 440 return (b->rp + b->rl) % b->bufsize; 441 } 442 443 uint64_t 444 sndbuf_getblocks(struct snd_dbuf *b) 445 { 446 return b->total / b->blksz; 447 } 448 449 unsigned int 450 sndbuf_xbytes(unsigned int v, struct snd_dbuf *from, struct snd_dbuf *to) 451 { 452 if (from == NULL || to == NULL || v == 0) 453 return 0; 454 455 return snd_xbytes(v, from->align * from->spd, to->align * to->spd); 456 } 457 458 uint8_t 459 sndbuf_zerodata(uint32_t fmt) 460 { 461 if (fmt & (AFMT_SIGNED | AFMT_PASSTHROUGH)) 462 return (0x00); 463 else if (fmt & AFMT_MU_LAW) 464 return (0x7f); 465 else if (fmt & AFMT_A_LAW) 466 return (0x55); 467 return (0x80); 468 } 469 470 /************************************************************/ 471 472 /** 473 * @brief Acquire buffer space to extend ready area 474 * 475 * This function extends the ready area length by @c count bytes, and may 476 * optionally copy samples from another location stored in @c from. The 477 * counter @c snd_dbuf::total is also incremented by @c count bytes. 478 * 479 * @param b audio buffer 480 * @param from sample source (optional) 481 * @param count number of bytes to acquire 482 * 483 * @retval 0 Unconditional 484 */ 485 int 486 sndbuf_acquire(struct snd_dbuf *b, uint8_t *from, unsigned int count) 487 { 488 int l; 489 490 KASSERT(count <= sndbuf_getfree(b), ("%s: count %d > free %d", __func__, count, sndbuf_getfree(b))); 491 KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d", __func__, b->rl)); 492 b->total += count; 493 if (from != NULL) { 494 while (count > 0) { 495 l = min(count, b->bufsize - sndbuf_getfreeptr(b)); 496 bcopy(from, sndbuf_getbufofs(b, sndbuf_getfreeptr(b)), l); 497 from += l; 498 b->rl += l; 499 count -= l; 500 } 501 } else 502 b->rl += count; 503 KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d, count %d", __func__, b->rl, count)); 504 505 return 0; 506 } 507 508 /** 509 * @brief Dispose samples from channel buffer, increasing size of ready area 510 * 511 * This function discards samples from the supplied buffer by advancing the 512 * ready area start pointer and decrementing the ready area length. If 513 * @c to is not NULL, then the discard samples will be copied to the location 514 * it points to. 515 * 516 * @param b PCM channel sound buffer 517 * @param to destination buffer (optional) 518 * @param count number of bytes to discard 519 * 520 * @returns 0 unconditionally 521 */ 522 int 523 sndbuf_dispose(struct snd_dbuf *b, uint8_t *to, unsigned int count) 524 { 525 int l; 526 527 KASSERT(count <= sndbuf_getready(b), ("%s: count %d > ready %d", __func__, count, sndbuf_getready(b))); 528 KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d", __func__, b->rl)); 529 if (to != NULL) { 530 while (count > 0) { 531 l = min(count, b->bufsize - sndbuf_getreadyptr(b)); 532 bcopy(sndbuf_getbufofs(b, sndbuf_getreadyptr(b)), to, l); 533 to += l; 534 b->rl -= l; 535 b->rp = (b->rp + l) % b->bufsize; 536 count -= l; 537 } 538 } else { 539 b->rl -= count; 540 b->rp = (b->rp + count) % b->bufsize; 541 } 542 KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d, count %d", __func__, b->rl, count)); 543 544 return 0; 545 } 546 547 /* count is number of bytes we want added to destination buffer */ 548 int 549 sndbuf_feed(struct snd_dbuf *from, struct snd_dbuf *to, struct pcm_channel *channel, struct pcm_feeder *feeder, unsigned int count) 550 { 551 unsigned int cnt, maxfeed; 552 553 KASSERT(count > 0, ("can't feed 0 bytes")); 554 555 if (sndbuf_getfree(to) < count) 556 return (EINVAL); 557 558 maxfeed = SND_FXROUND(SND_FXDIV_MAX, to->align); 559 560 do { 561 cnt = FEEDER_FEED(feeder, channel, to->tmpbuf, 562 min(count, maxfeed), from); 563 if (cnt == 0) 564 break; 565 sndbuf_acquire(to, to->tmpbuf, cnt); 566 count -= cnt; 567 } while (count != 0); 568 569 return (0); 570 } 571 572 /** 573 * @brief Clear the shadow buffer by filling with samples equal to zero. 574 * 575 * @param b buffer to clear 576 */ 577 void 578 sndbuf_clearshadow(struct snd_dbuf *b) 579 { 580 KASSERT(b != NULL, ("b is a null pointer")); 581 KASSERT(b->sl >= 0, ("illegal shadow length")); 582 583 if ((b->shadbuf != NULL) && (b->sl > 0)) 584 memset(b->shadbuf, sndbuf_zerodata(b->fmt), b->sl); 585 } 586 587 #ifdef OSSV4_EXPERIMENT 588 /** 589 * @brief Return peak value from samples in buffer ready area. 590 * 591 * Peak ranges from 0-32767. If channel is monaural, most significant 16 592 * bits will be zero. For now, only expects to work with 1-2 channel 593 * buffers. 594 * 595 * @note Currently only operates with linear PCM formats. 596 * 597 * @param b buffer to analyze 598 * @param lpeak pointer to store left peak value 599 * @param rpeak pointer to store right peak value 600 */ 601 void 602 sndbuf_getpeaks(struct snd_dbuf *b, int *lp, int *rp) 603 { 604 uint32_t lpeak, rpeak; 605 606 lpeak = 0; 607 rpeak = 0; 608 609 /** 610 * @todo fill this in later 611 */ 612 } 613 #endif 614