/*
 * eqchurn.c — kernel-side churn against the live feeder_eq (guest /dev/dsp,
 * /dev/mixer) as an unprivileged user. Hammers the FEEDEQ state machine via
 * every unpriv-reachable input:
 *   - dsp format/channel/rate reconfigs (feeder chain rebuilds incl. EQ)
 *   - mixer SOUND_MIXER_TREBLE/BASS (mixer_set_eq -> FEEDEQ_TREBLE/BASS),
 *     including out-of-range 101..127 values
 *   - sustained write()s with EQ enabled (biquad hot loop in-kernel)
 *   - concurrent threads to shake the sysctl-walk vs feed paths
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>

static volatile int stop;

static int
open_dsp(void)
{
	int fd = open("/dev/dsp", O_RDWR | O_NONBLOCK);
	if (fd < 0)
		fd = open("/dev/dsp", O_WRONLY | O_NONBLOCK);
	return (fd);
}

static void *
dsp_thr(void *arg)
{
	short buf[2048];
	int fd, iter = 0, i;
	static const int chns[] = { 1, 2, 4, 6, 8, 3, 5, 7, 2 };
	static const int rates[] = { 44100, 48000, 22050, 96000, 8000, 192000,
	    46050, 46051, 11025 };
	static const int fmts[] = { AFMT_S16_LE, AFMT_S32_LE, AFMT_S16_BE,
	    AFMT_U16_LE };

	(void)arg;
	for (i = 0; i < 2048; i++)
		buf[i] = (short)(i * 2654435761u);
	while (!stop) {
		fd = open_dsp();
		if (fd < 0) {
			usleep(10000);
			continue;
		}
		int ch = chns[iter % 9];
		int rate = rates[iter % 9];
		int fmt = fmts[iter % 4];
		ioctl(fd, SNDCTL_DSP_RESET, NULL);
		int v = fmt;		ioctl(fd, SNDCTL_DSP_SETFMT, &v);
		v = ch;		ioctl(fd, SNDCTL_DSP_CHANNELS, &v);
		v = rate;		ioctl(fd, SNDCTL_DSP_SPEED, &v);
		int trig = 0;		ioctl(fd, SNDCTL_DSP_SETTRIGGER, &trig);
		trig = PCM_ENABLE_OUTPUT;
		ioctl(fd, SNDCTL_DSP_SETTRIGGER, &trig);
		for (i = 0; i < 40 && !stop; i++) {
			ssize_t n = write(fd, buf, sizeof(buf));
			(void)n;
		}
		/* partial-block writes with odd sizes */
		for (i = 0; i < 20 && !stop; i++) {
			size_t sz = 1 + ((size_t)(iter * 131 + i * 7) % 8192);
			ssize_t n = write(fd, buf, sz > sizeof(buf) ?
			    sizeof(buf) : sz);
			(void)n;
		}
		close(fd);
		iter++;
	}
	return (NULL);
}

static void *
mix_thr(void *arg)
{
	int fd, iter = 0;
	static const int levs[] = { 0, 50, 100, 101, 127, 128, 255, 0x7f7f,
	    0x6565, 0x6464, 0x6b6b, -1 };

	(void)arg;
	while (!stop) {
		fd = open("/dev/mixer", O_RDWR);
		if (fd < 0) {
			usleep(20000);
			continue;
		}
		for (int i = 0; i < 16 && !stop; i++) {
			int lev = levs[(iter + i) % 12];
			ioctl(fd, MIXER_WRITE(SOUND_MIXER_TREBLE), &lev);
			ioctl(fd, MIXER_WRITE(SOUND_MIXER_BASS), &lev);
			ioctl(fd, MIXER_WRITE(SOUND_MIXER_PCM), &lev);
		}
		close(fd);
		iter++;
	}
	return (NULL);
}

int
main(int argc, char **argv)
{
	int secs = argc > 1 ? atoi(argv[1]) : 20;
	pthread_t t[4];

	for (int i = 0; i < 3; i++)
		pthread_create(&t[i], NULL, dsp_thr, NULL);
	pthread_create(&t[3], NULL, mix_thr, NULL);
	sleep(secs);
	stop = 1;
	for (int i = 0; i < 4; i++)
		pthread_join(t[i], NULL);
	printf("eqchurn done (%d s), errno last=%d\n", secs, errno);
	return (0);
}
