#include <sys/sndstat.h>
#include <sys/nv.h>

#include <err.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>

int
main(int argc, char *argv[])
{
	nvlist_t *nvl;
	const nvlist_t * const *di;
	struct sndstioc_nv_arg arg;
	size_t nitems;
	int fd;
	int pchan, rchan, pvchan, rvchan, chans;

	if ((fd = open("/dev/sndstat", O_RDONLY)) < 0)
		err(1, "open");
	if (ioctl(fd, SNDSTIOC_REFRESH_DEVS, NULL) < 0)
		err(1, "ioctl(SNDSTIOC_REFRESH_DEVS)");

	arg.nbytes = 0;
	arg.buf = NULL;
	if (ioctl(fd, SNDSTIOC_GET_DEVS, &arg) < 0)
		err(1, "(1) ioctl(SNDSTIOC_GET_DEVS)");
	if ((arg.buf = malloc(arg.nbytes)) == NULL)
		err(1, "malloc");
	if (ioctl(fd, SNDSTIOC_GET_DEVS, &arg) < 0)
		err(1, "(2) ioctl(SNDSTIOC_GET_DEVS)");
	nvl = nvlist_unpack(arg.buf, arg.nbytes, 0);
	free(arg.buf);
	di = nvlist_get_nvlist_array(nvl, SNDST_DSPS, &nitems);
	for (size_t i = 0; i < nitems; i++) {
		const nvlist_t * const *cdi;
		size_t nchans;

		printf("nameunit=%s\n", nvlist_get_string(di[i],
		    SNDST_DSPS_NAMEUNIT));
		printf("from_user=%d\n", nvlist_get_bool(di[i],
		    SNDST_DSPS_FROM_USER));
		printf("devnode=%s\n", nvlist_get_string(di[i],
		    SNDST_DSPS_DEVNODE));
		printf("desc=%s\n", nvlist_get_string(di[i],
		    SNDST_DSPS_DESC));
		pchan = nvlist_get_number(di[i], SNDST_DSPS_PCHAN);
		rchan = nvlist_get_number(di[i], SNDST_DSPS_RCHAN);

		if (pchan) {
			printf("INFO_PLAY\n");
			printf("\tplay_min_rate=%lu\n",
			    nvlist_get_number(nvlist_get_nvlist(di[i],
			    SNDST_DSPS_INFO_PLAY), SNDST_DSPS_INFO_MIN_RATE));
			printf("\tplay_max_rate=%lu\n",
			    nvlist_get_number(nvlist_get_nvlist(di[i],
			    SNDST_DSPS_INFO_PLAY), SNDST_DSPS_INFO_MAX_RATE));
			printf("\tplay_formats=%#08lx\n",
			    nvlist_get_number(nvlist_get_nvlist(di[i],
			    SNDST_DSPS_INFO_PLAY), SNDST_DSPS_INFO_FORMATS));
			printf("\tplay_min_chn=%lu\n",
			    nvlist_get_number(nvlist_get_nvlist(di[i],
			    SNDST_DSPS_INFO_PLAY), SNDST_DSPS_INFO_MIN_CHN));
			printf("\tplay_max_chn=%lu\n",
			    nvlist_get_number(nvlist_get_nvlist(di[i],
			    SNDST_DSPS_INFO_PLAY), SNDST_DSPS_INFO_MAX_CHN));
		}
		if (rchan) {
			printf("INFO_REC\n");
			printf("\trec_min_rate=%lu\n",
			    nvlist_get_number(nvlist_get_nvlist(di[i],
			    SNDST_DSPS_INFO_REC), SNDST_DSPS_INFO_MIN_RATE));
			printf("\trec_max_rate=%lu\n",
			    nvlist_get_number(nvlist_get_nvlist(di[i],
			    SNDST_DSPS_INFO_REC), SNDST_DSPS_INFO_MAX_RATE));
			printf("\trec_formats=%#08lx\n",
			    nvlist_get_number(nvlist_get_nvlist(di[i],
			    SNDST_DSPS_INFO_REC), SNDST_DSPS_INFO_FORMATS));
			printf("\trec_min_chn=%lu\n",
			    nvlist_get_number(nvlist_get_nvlist(di[i],
			    SNDST_DSPS_INFO_REC), SNDST_DSPS_INFO_MIN_CHN));
			printf("\trec_max_chn=%lu\n",
			    nvlist_get_number(nvlist_get_nvlist(di[i],
			    SNDST_DSPS_INFO_REC), SNDST_DSPS_INFO_MAX_CHN));
		}

		if (!nvlist_exists(di[i], SNDST_DSPS_PROVIDER_INFO))
			continue;
		pvchan = nvlist_get_number(
		    nvlist_get_nvlist(di[i], SNDST_DSPS_PROVIDER_INFO),
		    SNDST_DSPS_SOUND4_PVCHAN);
		rvchan = nvlist_get_number(
		    nvlist_get_nvlist(di[i], SNDST_DSPS_PROVIDER_INFO),
		    SNDST_DSPS_SOUND4_RVCHAN);
		chans = pchan + rchan + pvchan + rvchan;

		printf("pchan=%d\n", pchan);
		printf("rchan=%d\n", rchan);
		printf("pvchan=%d\n", pvchan);
		printf("rvchan=%d\n", rvchan);
		printf("chans=%d\n", chans);

		cdi = nvlist_get_nvlist_array(
		    nvlist_get_nvlist(di[i], SNDST_DSPS_PROVIDER_INFO),
		    SNDST_DSPS_SOUND4_CHAN_INFO, &nchans);
		for (size_t j = 0; j < nchans; j++) {
			printf("chan=%s\n", nvlist_get_string(cdi[j],
			    SNDST_DSPS_SOUND4_CHAN_NAME));
			printf("\tparentchan=%s\n", nvlist_get_string(cdi[j],
			    SNDST_DSPS_SOUND4_CHAN_PARENTCHAN));
			printf("\tunit=%d\n", (int)nvlist_get_number(cdi[j],
			    SNDST_DSPS_SOUND4_CHAN_UNIT));
			printf("\tlatency=%d\n", (int)nvlist_get_number(cdi[j],
			    SNDST_DSPS_SOUND4_CHAN_LATENCY));
			printf("\trate=%d\n", (int)nvlist_get_number(cdi[j],
			    SNDST_DSPS_SOUND4_CHAN_RATE));
			printf("\tformat=%#08x\n", (int)nvlist_get_number(cdi[j],
			    SNDST_DSPS_SOUND4_CHAN_FORMAT));
			printf("\tpid=%d\n", (int)nvlist_get_number(cdi[j],
			    SNDST_DSPS_SOUND4_CHAN_PID));
			printf("\tcomm=%s\n", nvlist_get_string(cdi[j],
			    SNDST_DSPS_SOUND4_CHAN_COMM));
			printf("\tinterrupts=%d\n", (int)nvlist_get_number(cdi[j],
			    SNDST_DSPS_SOUND4_CHAN_INTR));
			printf("\txruns=%d\n", (int)nvlist_get_number(cdi[j],
			    SNDST_DSPS_SOUND4_CHAN_XRUNS));
			printf("\tfeedcount=%d\n", (int)nvlist_get_number(cdi[j],
			    SNDST_DSPS_SOUND4_CHAN_FEEDCNT));
			printf("\tleftvol=%d\n", (int)nvlist_get_number(cdi[j],
			    SNDST_DSPS_SOUND4_CHAN_LEFTVOL));
			printf("\trightvol=%d\n", (int)nvlist_get_number(cdi[j],
			    SNDST_DSPS_SOUND4_CHAN_RIGHTVOL));
			printf("\thwbuf_fmt=%#08x\n", (int)nvlist_get_number(cdi[j],
			    SNDST_DSPS_SOUND4_CHAN_HWBUF_FORMAT));
			printf("\thwbuf_size=%d\n", (int)nvlist_get_number(cdi[j],
			    SNDST_DSPS_SOUND4_CHAN_HWBUF_SIZE));
			printf("\thwbuf_blksz=%d\n", (int)nvlist_get_number(cdi[j],
			    SNDST_DSPS_SOUND4_CHAN_HWBUF_BLKSZ));
			printf("\thwbuf_blkcnt=%d\n", (int)nvlist_get_number(cdi[j],
			    SNDST_DSPS_SOUND4_CHAN_HWBUF_BLKCNT));
			printf("\thwbuf_free=%d\n", (int)nvlist_get_number(cdi[j],
			    SNDST_DSPS_SOUND4_CHAN_HWBUF_FREE));
			printf("\thwbuf_ready=%d\n", (int)nvlist_get_number(cdi[j],
			    SNDST_DSPS_SOUND4_CHAN_HWBUF_READY));
			printf("\tswbuf_fmt=%#08x\n", (int)nvlist_get_number(cdi[j],
			    SNDST_DSPS_SOUND4_CHAN_SWBUF_FORMAT));
			printf("\tswbuf_size=%d\n", (int)nvlist_get_number(cdi[j],
			    SNDST_DSPS_SOUND4_CHAN_SWBUF_SIZE));
			printf("\tswbuf_blksz=%d\n", (int)nvlist_get_number(cdi[j],
			    SNDST_DSPS_SOUND4_CHAN_SWBUF_BLKSZ));
			printf("\tswbuf_blkcnt=%d\n", (int)nvlist_get_number(cdi[j],
			    SNDST_DSPS_SOUND4_CHAN_SWBUF_BLKCNT));
			printf("\tswbuf_free=%d\n", (int)nvlist_get_number(cdi[j],
			    SNDST_DSPS_SOUND4_CHAN_SWBUF_FREE));
			printf("\tswbuf_ready=%d\n", (int)nvlist_get_number(cdi[j],
			    SNDST_DSPS_SOUND4_CHAN_SWBUF_READY));
			printf("\tfeederchain=%s\n", nvlist_get_string(cdi[j],
			    SNDST_DSPS_SOUND4_CHAN_FEEDERCHAIN));
		}
	}
	nvlist_destroy(nvl);

	return (0);
}