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 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31#ifdef HAVE_KERNEL_OPTION_HEADERS
32#include "opt_snd.h"
33#endif
34
35#include <dev/sound/pcm/sound.h>
36
37#include "feeder_if.h"
38
39#define SND_USE_FXDIV
40#define	SND_DECLARE_FXDIV
41#include "snd_fxdiv_gen.h"
42
43struct snd_dbuf *
44sndbuf_create(device_t dev, char *drv, char *desc, struct pcm_channel *channel)
45{
46	struct snd_dbuf *b;
47
48	b = malloc(sizeof(*b), M_DEVBUF, M_WAITOK | M_ZERO);
49	snprintf(b->name, SNDBUF_NAMELEN, "%s:%s", drv, desc);
50	b->dev = dev;
51	b->channel = channel;
52
53	return b;
54}
55
56void
57sndbuf_destroy(struct snd_dbuf *b)
58{
59	sndbuf_free(b);
60	free(b, M_DEVBUF);
61}
62
63bus_addr_t
64sndbuf_getbufaddr(struct snd_dbuf *buf)
65{
66	return (buf->buf_addr);
67}
68
69static void
70sndbuf_setmap(void *arg, bus_dma_segment_t *segs, int nseg, int error)
71{
72	struct snd_dbuf *b = (struct snd_dbuf *)arg;
73
74	if (snd_verbose > 3) {
75		device_printf(b->dev, "sndbuf_setmap %lx, %lx; ",
76		    (u_long)segs[0].ds_addr, (u_long)segs[0].ds_len);
77		printf("%p -> %lx\n", b->buf, (u_long)segs[0].ds_addr);
78	}
79	if (error == 0)
80		b->buf_addr = segs[0].ds_addr;
81	else
82		b->buf_addr = 0;
83}
84
85/*
86 * Allocate memory for DMA buffer. If the device does not use DMA transfers,
87 * the driver can call malloc(9) and sndbuf_setup() itself.
88 */
89
90int
91sndbuf_alloc(struct snd_dbuf *b, bus_dma_tag_t dmatag, int dmaflags,
92    unsigned int size)
93{
94	int ret;
95
96	b->dmatag = dmatag;
97	b->dmaflags = dmaflags | BUS_DMA_NOWAIT | BUS_DMA_COHERENT;
98	b->maxsize = size;
99	b->bufsize = b->maxsize;
100	b->buf_addr = 0;
101	b->flags |= SNDBUF_F_MANAGED;
102	if (bus_dmamem_alloc(b->dmatag, (void **)&b->buf, b->dmaflags,
103	    &b->dmamap)) {
104		sndbuf_free(b);
105		return (ENOMEM);
106	}
107	if (bus_dmamap_load(b->dmatag, b->dmamap, b->buf, b->maxsize,
108	    sndbuf_setmap, b, BUS_DMA_NOWAIT) != 0 || b->buf_addr == 0) {
109		sndbuf_free(b);
110		return (ENOMEM);
111	}
112
113	ret = sndbuf_resize(b, 2, b->maxsize / 2);
114	if (ret != 0)
115		sndbuf_free(b);
116
117	return (ret);
118}
119
120int
121sndbuf_setup(struct snd_dbuf *b, void *buf, unsigned int size)
122{
123	b->flags &= ~SNDBUF_F_MANAGED;
124	if (buf)
125		b->flags |= SNDBUF_F_MANAGED;
126	b->buf = buf;
127	b->maxsize = size;
128	b->bufsize = b->maxsize;
129	return sndbuf_resize(b, 2, b->maxsize / 2);
130}
131
132void
133sndbuf_free(struct snd_dbuf *b)
134{
135	if (b->tmpbuf)
136		free(b->tmpbuf, M_DEVBUF);
137
138	if (b->shadbuf)
139		free(b->shadbuf, M_DEVBUF);
140
141	if (b->buf) {
142		if (b->flags & SNDBUF_F_MANAGED) {
143			if (b->buf_addr)
144				bus_dmamap_unload(b->dmatag, b->dmamap);
145			if (b->dmatag)
146				bus_dmamem_free(b->dmatag, b->buf, b->dmamap);
147		} else
148			free(b->buf, M_DEVBUF);
149	}
150	seldrain(sndbuf_getsel(b));
151
152	b->tmpbuf = NULL;
153	b->shadbuf = NULL;
154	b->buf = NULL;
155	b->sl = 0;
156	b->dmatag = NULL;
157	b->dmamap = NULL;
158}
159
160#define SNDBUF_CACHE_SHIFT	5
161
162int
163sndbuf_resize(struct snd_dbuf *b, unsigned int blkcnt, unsigned int blksz)
164{
165	unsigned int bufsize, allocsize;
166	u_int8_t *tmpbuf;
167
168	CHN_LOCK(b->channel);
169	if (b->maxsize == 0)
170		goto out;
171	if (blkcnt == 0)
172		blkcnt = b->blkcnt;
173	if (blksz == 0)
174		blksz = b->blksz;
175	if (blkcnt < 2 || blksz < 16 || (blkcnt * blksz) > b->maxsize) {
176		CHN_UNLOCK(b->channel);
177		return EINVAL;
178	}
179	if (blkcnt == b->blkcnt && blksz == b->blksz)
180		goto out;
181
182	bufsize = blkcnt * blksz;
183
184	if (bufsize > b->allocsize ||
185	    bufsize < (b->allocsize >> SNDBUF_CACHE_SHIFT)) {
186		allocsize = round_page(bufsize);
187		CHN_UNLOCK(b->channel);
188		tmpbuf = malloc(allocsize, M_DEVBUF, M_WAITOK);
189		CHN_LOCK(b->channel);
190		if (snd_verbose > 3)
191			printf("%s(): b=%p %p -> %p [%d -> %d : %d]\n",
192			    __func__, b, b->tmpbuf, tmpbuf,
193			    b->allocsize, allocsize, bufsize);
194		if (b->tmpbuf != NULL)
195			free(b->tmpbuf, M_DEVBUF);
196		b->tmpbuf = tmpbuf;
197		b->allocsize = allocsize;
198	} else if (snd_verbose > 3)
199		printf("%s(): b=%p %d [%d] NOCHANGE\n",
200		    __func__, b, b->allocsize, b->bufsize);
201
202	b->blkcnt = blkcnt;
203	b->blksz = blksz;
204	b->bufsize = bufsize;
205
206	sndbuf_reset(b);
207out:
208	CHN_UNLOCK(b->channel);
209	return 0;
210}
211
212int
213sndbuf_remalloc(struct snd_dbuf *b, unsigned int blkcnt, unsigned int blksz)
214{
215        unsigned int bufsize, allocsize;
216	u_int8_t *buf, *tmpbuf, *shadbuf;
217
218	if (blkcnt < 2 || blksz < 16)
219		return EINVAL;
220
221	bufsize = blksz * blkcnt;
222
223	if (bufsize > b->allocsize ||
224	    bufsize < (b->allocsize >> SNDBUF_CACHE_SHIFT)) {
225		allocsize = round_page(bufsize);
226		CHN_UNLOCK(b->channel);
227		buf = malloc(allocsize, M_DEVBUF, M_WAITOK);
228		tmpbuf = malloc(allocsize, M_DEVBUF, M_WAITOK);
229		shadbuf = malloc(allocsize, M_DEVBUF, M_WAITOK);
230		CHN_LOCK(b->channel);
231		if (b->buf != NULL)
232			free(b->buf, M_DEVBUF);
233		b->buf = buf;
234		if (b->tmpbuf != NULL)
235			free(b->tmpbuf, M_DEVBUF);
236		b->tmpbuf = tmpbuf;
237		if (b->shadbuf != NULL)
238			free(b->shadbuf, M_DEVBUF);
239		b->shadbuf = shadbuf;
240		if (snd_verbose > 3)
241			printf("%s(): b=%p %d -> %d [%d]\n",
242			    __func__, b, b->allocsize, allocsize, bufsize);
243		b->allocsize = allocsize;
244	} else if (snd_verbose > 3)
245		printf("%s(): b=%p %d [%d] NOCHANGE\n",
246		    __func__, b, b->allocsize, b->bufsize);
247
248	b->blkcnt = blkcnt;
249	b->blksz = blksz;
250	b->bufsize = bufsize;
251	b->maxsize = bufsize;
252	b->sl = bufsize;
253
254	sndbuf_reset(b);
255
256	return 0;
257}
258
259/**
260 * @brief Zero out space in buffer free area
261 *
262 * This function clears a chunk of @c length bytes in the buffer free area
263 * (i.e., where the next write will be placed).
264 *
265 * @param b		buffer context
266 * @param length	number of bytes to blank
267 */
268void
269sndbuf_clear(struct snd_dbuf *b, unsigned int length)
270{
271	int i;
272	u_char data, *p;
273
274	if (length == 0)
275		return;
276	if (length > b->bufsize)
277		length = b->bufsize;
278
279	data = sndbuf_zerodata(b->fmt);
280
281	i = sndbuf_getfreeptr(b);
282	p = sndbuf_getbuf(b);
283	while (length > 0) {
284		p[i] = data;
285		length--;
286		i++;
287		if (i >= b->bufsize)
288			i = 0;
289	}
290}
291
292/**
293 * @brief Zap buffer contents, resetting "ready area" fields
294 *
295 * @param b	buffer context
296 */
297void
298sndbuf_fillsilence(struct snd_dbuf *b)
299{
300	if (b->bufsize > 0)
301		memset(sndbuf_getbuf(b), sndbuf_zerodata(b->fmt), b->bufsize);
302	b->rp = 0;
303	b->rl = b->bufsize;
304}
305
306void
307sndbuf_fillsilence_rl(struct snd_dbuf *b, u_int rl)
308{
309	if (b->bufsize > 0)
310		memset(sndbuf_getbuf(b), sndbuf_zerodata(b->fmt), b->bufsize);
311	b->rp = 0;
312	b->rl = min(b->bufsize, rl);
313}
314
315/**
316 * @brief Reset buffer w/o flushing statistics
317 *
318 * This function just zeroes out buffer contents and sets the "ready length"
319 * to zero.  This was originally to facilitate minimal playback interruption
320 * (i.e., dropped samples) in SNDCTL_DSP_SILENCE/SKIP ioctls.
321 *
322 * @param b	buffer context
323 */
324void
325sndbuf_softreset(struct snd_dbuf *b)
326{
327	b->rl = 0;
328	if (b->buf && b->bufsize > 0)
329		sndbuf_clear(b, b->bufsize);
330}
331
332void
333sndbuf_reset(struct snd_dbuf *b)
334{
335	b->hp = 0;
336	b->rp = 0;
337	b->rl = 0;
338	b->dl = 0;
339	b->prev_total = 0;
340	b->total = 0;
341	b->xrun = 0;
342	if (b->buf && b->bufsize > 0)
343		sndbuf_clear(b, b->bufsize);
344	sndbuf_clearshadow(b);
345}
346
347u_int32_t
348sndbuf_getfmt(struct snd_dbuf *b)
349{
350	return b->fmt;
351}
352
353int
354sndbuf_setfmt(struct snd_dbuf *b, u_int32_t fmt)
355{
356	b->fmt = fmt;
357	b->bps = AFMT_BPS(b->fmt);
358	b->align = AFMT_ALIGN(b->fmt);
359#if 0
360	b->bps = AFMT_CHANNEL(b->fmt);
361	if (b->fmt & AFMT_16BIT)
362		b->bps <<= 1;
363	else if (b->fmt & AFMT_24BIT)
364		b->bps *= 3;
365	else if (b->fmt & AFMT_32BIT)
366		b->bps <<= 2;
367#endif
368	return 0;
369}
370
371unsigned int
372sndbuf_getspd(struct snd_dbuf *b)
373{
374	return b->spd;
375}
376
377void
378sndbuf_setspd(struct snd_dbuf *b, unsigned int spd)
379{
380	b->spd = spd;
381}
382
383unsigned int
384sndbuf_getalign(struct snd_dbuf *b)
385{
386	return (b->align);
387}
388
389unsigned int
390sndbuf_getblkcnt(struct snd_dbuf *b)
391{
392	return b->blkcnt;
393}
394
395void
396sndbuf_setblkcnt(struct snd_dbuf *b, unsigned int blkcnt)
397{
398	b->blkcnt = blkcnt;
399}
400
401unsigned int
402sndbuf_getblksz(struct snd_dbuf *b)
403{
404	return b->blksz;
405}
406
407void
408sndbuf_setblksz(struct snd_dbuf *b, unsigned int blksz)
409{
410	b->blksz = blksz;
411}
412
413unsigned int
414sndbuf_getbps(struct snd_dbuf *b)
415{
416	return b->bps;
417}
418
419void *
420sndbuf_getbuf(struct snd_dbuf *b)
421{
422	return b->buf;
423}
424
425void *
426sndbuf_getbufofs(struct snd_dbuf *b, unsigned int ofs)
427{
428	KASSERT(ofs < b->bufsize, ("%s: ofs invalid %d", __func__, ofs));
429
430	return b->buf + ofs;
431}
432
433unsigned int
434sndbuf_getsize(struct snd_dbuf *b)
435{
436	return b->bufsize;
437}
438
439unsigned int
440sndbuf_getmaxsize(struct snd_dbuf *b)
441{
442	return b->maxsize;
443}
444
445unsigned int
446sndbuf_getallocsize(struct snd_dbuf *b)
447{
448	return b->allocsize;
449}
450
451unsigned int
452sndbuf_runsz(struct snd_dbuf *b)
453{
454	return b->dl;
455}
456
457void
458sndbuf_setrun(struct snd_dbuf *b, int go)
459{
460	b->dl = go? b->blksz : 0;
461}
462
463struct selinfo *
464sndbuf_getsel(struct snd_dbuf *b)
465{
466	return &b->sel;
467}
468
469/************************************************************/
470unsigned int
471sndbuf_getxrun(struct snd_dbuf *b)
472{
473	SNDBUF_LOCKASSERT(b);
474
475	return b->xrun;
476}
477
478void
479sndbuf_setxrun(struct snd_dbuf *b, unsigned int xrun)
480{
481	SNDBUF_LOCKASSERT(b);
482
483	b->xrun = xrun;
484}
485
486unsigned int
487sndbuf_gethwptr(struct snd_dbuf *b)
488{
489	SNDBUF_LOCKASSERT(b);
490
491	return b->hp;
492}
493
494void
495sndbuf_sethwptr(struct snd_dbuf *b, unsigned int ptr)
496{
497	SNDBUF_LOCKASSERT(b);
498
499	b->hp = ptr;
500}
501
502unsigned int
503sndbuf_getready(struct snd_dbuf *b)
504{
505	SNDBUF_LOCKASSERT(b);
506	KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d", __func__, b->rl));
507
508	return b->rl;
509}
510
511unsigned int
512sndbuf_getreadyptr(struct snd_dbuf *b)
513{
514	SNDBUF_LOCKASSERT(b);
515	KASSERT((b->rp >= 0) && (b->rp <= b->bufsize), ("%s: b->rp invalid %d", __func__, b->rp));
516
517	return b->rp;
518}
519
520unsigned int
521sndbuf_getfree(struct snd_dbuf *b)
522{
523	SNDBUF_LOCKASSERT(b);
524	KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d", __func__, b->rl));
525
526	return b->bufsize - b->rl;
527}
528
529unsigned int
530sndbuf_getfreeptr(struct snd_dbuf *b)
531{
532	SNDBUF_LOCKASSERT(b);
533	KASSERT((b->rp >= 0) && (b->rp <= b->bufsize), ("%s: b->rp invalid %d", __func__, b->rp));
534	KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d", __func__, b->rl));
535
536	return (b->rp + b->rl) % b->bufsize;
537}
538
539u_int64_t
540sndbuf_getblocks(struct snd_dbuf *b)
541{
542	SNDBUF_LOCKASSERT(b);
543
544	return b->total / b->blksz;
545}
546
547u_int64_t
548sndbuf_getprevblocks(struct snd_dbuf *b)
549{
550	SNDBUF_LOCKASSERT(b);
551
552	return b->prev_total / b->blksz;
553}
554
555u_int64_t
556sndbuf_gettotal(struct snd_dbuf *b)
557{
558	SNDBUF_LOCKASSERT(b);
559
560	return b->total;
561}
562
563u_int64_t
564sndbuf_getprevtotal(struct snd_dbuf *b)
565{
566	SNDBUF_LOCKASSERT(b);
567
568	return b->prev_total;
569}
570
571void
572sndbuf_updateprevtotal(struct snd_dbuf *b)
573{
574	SNDBUF_LOCKASSERT(b);
575
576	b->prev_total = b->total;
577}
578
579unsigned int
580sndbuf_xbytes(unsigned int v, struct snd_dbuf *from, struct snd_dbuf *to)
581{
582	if (from == NULL || to == NULL || v == 0)
583		return 0;
584
585	return snd_xbytes(v, sndbuf_getalign(from) * sndbuf_getspd(from),
586	    sndbuf_getalign(to) * sndbuf_getspd(to));
587}
588
589u_int8_t
590sndbuf_zerodata(u_int32_t fmt)
591{
592	if (fmt & (AFMT_SIGNED | AFMT_PASSTHROUGH))
593		return (0x00);
594	else if (fmt & AFMT_MU_LAW)
595		return (0x7f);
596	else if (fmt & AFMT_A_LAW)
597		return (0x55);
598	return (0x80);
599}
600
601/************************************************************/
602
603/**
604 * @brief Acquire buffer space to extend ready area
605 *
606 * This function extends the ready area length by @c count bytes, and may
607 * optionally copy samples from another location stored in @c from.  The
608 * counter @c snd_dbuf::total is also incremented by @c count bytes.
609 *
610 * @param b	audio buffer
611 * @param from	sample source (optional)
612 * @param count	number of bytes to acquire
613 *
614 * @retval 0	Unconditional
615 */
616int
617sndbuf_acquire(struct snd_dbuf *b, u_int8_t *from, unsigned int count)
618{
619	int l;
620
621	KASSERT(count <= sndbuf_getfree(b), ("%s: count %d > free %d", __func__, count, sndbuf_getfree(b)));
622	KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d", __func__, b->rl));
623	b->total += count;
624	if (from != NULL) {
625		while (count > 0) {
626			l = min(count, sndbuf_getsize(b) - sndbuf_getfreeptr(b));
627			bcopy(from, sndbuf_getbufofs(b, sndbuf_getfreeptr(b)), l);
628			from += l;
629			b->rl += l;
630			count -= l;
631		}
632	} else
633		b->rl += count;
634	KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d, count %d", __func__, b->rl, count));
635
636	return 0;
637}
638
639/**
640 * @brief Dispose samples from channel buffer, increasing size of ready area
641 *
642 * This function discards samples from the supplied buffer by advancing the
643 * ready area start pointer and decrementing the ready area length.  If
644 * @c to is not NULL, then the discard samples will be copied to the location
645 * it points to.
646 *
647 * @param b	PCM channel sound buffer
648 * @param to	destination buffer (optional)
649 * @param count	number of bytes to discard
650 *
651 * @returns 0 unconditionally
652 */
653int
654sndbuf_dispose(struct snd_dbuf *b, u_int8_t *to, unsigned int count)
655{
656	int l;
657
658	KASSERT(count <= sndbuf_getready(b), ("%s: count %d > ready %d", __func__, count, sndbuf_getready(b)));
659	KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d", __func__, b->rl));
660	if (to != NULL) {
661		while (count > 0) {
662			l = min(count, sndbuf_getsize(b) - sndbuf_getreadyptr(b));
663			bcopy(sndbuf_getbufofs(b, sndbuf_getreadyptr(b)), to, l);
664			to += l;
665			b->rl -= l;
666			b->rp = (b->rp + l) % b->bufsize;
667			count -= l;
668		}
669	} else {
670		b->rl -= count;
671		b->rp = (b->rp + count) % b->bufsize;
672	}
673	KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d, count %d", __func__, b->rl, count));
674
675	return 0;
676}
677
678#ifdef SND_DIAGNOSTIC
679static uint32_t snd_feeder_maxfeed = 0;
680SYSCTL_UINT(_hw_snd, OID_AUTO, feeder_maxfeed, CTLFLAG_RD,
681    &snd_feeder_maxfeed, 0, "maximum feeder count request");
682
683static uint32_t snd_feeder_maxcycle = 0;
684SYSCTL_UINT(_hw_snd, OID_AUTO, feeder_maxcycle, CTLFLAG_RD,
685    &snd_feeder_maxcycle, 0, "maximum feeder cycle");
686#endif
687
688/* count is number of bytes we want added to destination buffer */
689int
690sndbuf_feed(struct snd_dbuf *from, struct snd_dbuf *to, struct pcm_channel *channel, struct pcm_feeder *feeder, unsigned int count)
691{
692	unsigned int cnt, maxfeed;
693#ifdef SND_DIAGNOSTIC
694	unsigned int cycle;
695
696	if (count > snd_feeder_maxfeed)
697		snd_feeder_maxfeed = count;
698
699	cycle = 0;
700#endif
701
702	KASSERT(count > 0, ("can't feed 0 bytes"));
703
704	if (sndbuf_getfree(to) < count)
705		return (EINVAL);
706
707	maxfeed = SND_FXROUND(SND_FXDIV_MAX, sndbuf_getalign(to));
708
709	do {
710		cnt = FEEDER_FEED(feeder, channel, to->tmpbuf,
711		    min(count, maxfeed), from);
712		if (cnt == 0)
713			break;
714		sndbuf_acquire(to, to->tmpbuf, cnt);
715		count -= cnt;
716#ifdef SND_DIAGNOSTIC
717		cycle++;
718#endif
719	} while (count != 0);
720
721#ifdef SND_DIAGNOSTIC
722	if (cycle > snd_feeder_maxcycle)
723		snd_feeder_maxcycle = cycle;
724#endif
725
726	return (0);
727}
728
729/************************************************************/
730
731void
732sndbuf_dump(struct snd_dbuf *b, char *s, u_int32_t what)
733{
734	printf("%s: [", s);
735	if (what & 0x01)
736		printf(" bufsize: %d, maxsize: %d", b->bufsize, b->maxsize);
737	if (what & 0x02)
738		printf(" dl: %d, rp: %d, rl: %d, hp: %d", b->dl, b->rp, b->rl, b->hp);
739	if (what & 0x04)
740		printf(" total: %ju, prev_total: %ju, xrun: %d", (uintmax_t)b->total, (uintmax_t)b->prev_total, b->xrun);
741   	if (what & 0x08)
742		printf(" fmt: 0x%x, spd: %d", b->fmt, b->spd);
743	if (what & 0x10)
744		printf(" blksz: %d, blkcnt: %d, flags: 0x%x", b->blksz, b->blkcnt, b->flags);
745	printf(" ]\n");
746}
747
748/************************************************************/
749u_int32_t
750sndbuf_getflags(struct snd_dbuf *b)
751{
752	return b->flags;
753}
754
755void
756sndbuf_setflags(struct snd_dbuf *b, u_int32_t flags, int on)
757{
758	b->flags &= ~flags;
759	if (on)
760		b->flags |= flags;
761}
762
763/**
764 * @brief Clear the shadow buffer by filling with samples equal to zero.
765 *
766 * @param b buffer to clear
767 */
768void
769sndbuf_clearshadow(struct snd_dbuf *b)
770{
771	KASSERT(b != NULL, ("b is a null pointer"));
772	KASSERT(b->sl >= 0, ("illegal shadow length"));
773
774	if ((b->shadbuf != NULL) && (b->sl > 0))
775		memset(b->shadbuf, sndbuf_zerodata(b->fmt), b->sl);
776}
777
778#ifdef OSSV4_EXPERIMENT
779/**
780 * @brief Return peak value from samples in buffer ready area.
781 *
782 * Peak ranges from 0-32767.  If channel is monaural, most significant 16
783 * bits will be zero.  For now, only expects to work with 1-2 channel
784 * buffers.
785 *
786 * @note  Currently only operates with linear PCM formats.
787 *
788 * @param b buffer to analyze
789 * @param lpeak pointer to store left peak value
790 * @param rpeak pointer to store right peak value
791 */
792void
793sndbuf_getpeaks(struct snd_dbuf *b, int *lp, int *rp)
794{
795	u_int32_t lpeak, rpeak;
796
797	lpeak = 0;
798	rpeak = 0;
799
800	/**
801	 * @todo fill this in later
802	 */
803}
804#endif
805