/*
 * DF-4115/4116 — drive hblkcnt ABOVE HDA_BDL_MAX and verify the hdaa
 * clamp is the operative bound on the BDL walk.
 *
 * chn_resizebuf (channel.c:1832) computes the hardware request as
 *   hblkcnt = round_pow2(previous soft blkcnt)
 * so: SETFRAGMENT #1 sets soft blkcnt = 8192 (frag 0x7fff0000);
 * SETFRAGMENT #2 then hands hdaa hblkcnt=8192 (post-shrink 512 @ blksz 128)
 * — hdaa_channel_setfragments must clamp to HDA_BDL_MAX=256.
 * Then start the stream (write) — the BDL walk runs with blkcnt=256 max.
 * Watch dmesg (hw.snd.verbose=4): expect b[32768/128/256].
 */
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>

int main(void)
{
	setvbuf(stdout, NULL, _IONBF, 0);
	char junk[65536];
	memset(junk, 0x41, sizeof(junk));
	int fd = open("/dev/dsp0", O_WRONLY);
	if (fd < 0) { printf("[-] open: %s\n", strerror(errno)); return 2; }
	int fmt = AFMT_S16_LE, ch = 2, sp = 48000;
	audio_buf_info oi;
	(void)ioctl(fd, SNDCTL_DSP_SETFMT, &fmt);
	(void)ioctl(fd, SNDCTL_DSP_CHANNELS, &ch);
	(void)ioctl(fd, SNDCTL_DSP_SPEED, &sp);

	int f1 = 0x7fff0000;			/* soft blkcnt := 8192 */
	(void)ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &f1);
	memset(&oi, 0, sizeof(oi));
	(void)ioctl(fd, SNDCTL_DSP_GETOSPACE, &oi);
	printf("[1] frag %#x -> soft %d/%d/%d (soft blkcnt now 8192)\n",
	    f1, oi.fragsize, oi.fragstotal, oi.bytes);

	int f2 = 0x00020001;			/* forces hblkcnt=round_pow2(8192) */
	(void)ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &f2);
	memset(&oi, 0, sizeof(oi));
	(void)ioctl(fd, SNDCTL_DSP_GETOSPACE, &oi);
	printf("[2] frag %#x -> soft %d/%d/%d (hw request blkcnt=8192->512)\n",
	    f2, oi.fragsize, oi.fragstotal, oi.bytes);

	/* Start the stream: hdac_stream_start BDL walk with final blkcnt */
	ssize_t n = write(fd, junk, sizeof(junk));
	printf("[3] wrote %zd bytes — stream started; BDL walk ran\n", n);
	count_info ci;
	(void)ioctl(fd, SNDCTL_DSP_GETOPTR, &ci);
	printf("[4] getptr ok bytes=%d\n", ci.bytes);
	close(fd);
	printf("[+] double-fragment hblkcnt>256 trigger done, guest alive\n");
	return 0;
}
