xref: /freebsd/sys/dev/sound/pcm/buffer.c (revision d59c7ea2701fe7b73b32eef49a7c712ef38de5a0)
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