/*
 * DF-4129 harness — fakempu.ko
 *
 * Registers 32 fake MPU-401-style UART MIDI units through the exported
 * midi_init() API (sys/dev/sound/midi/midi.c:274) so that a sequencer
 * open enumerates midi_number == 32 (the maximum that does NOT trigger
 * the known DF-1394 midis[32] heap overflow).  Each unit appears as
 * /dev/midi<unit>.<channel> (0666).
 *
 * This simulates the "machine with many MIDI ports" configuration
 * (e.g. a pro-audio box with multiple mpu401 UARTs) — the only
 * precondition seq_sync()'s sync[16] overflow needs besides an open
 * /dev/sequencer.  Loading a kld requires root; USING /dev/sequencer
 * does not — the vulnerability trigger itself runs unprivileged.
 *
 * Build: see build.sh (compiled inside the kernel module obj tree so the
 * generated mpu_if.h / kobj headers resolve).
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/kobj.h>
#include <sys/module.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/malloc.h>

#include <dev/sound/midi/midi.h>
#include "mpu_if.h"

#define FAKE_UNITS	32	/* 32 => sync[16..31] OOB, midis[] in-bounds */

struct snd_midi *fake_units[FAKE_UNITS];

static int
fakempu_inqsize(struct snd_midi *kobj, void *cookie)
{
	return (1024);
}

static int
fakempu_outqsize(struct snd_midi *kobj, void *cookie)
{
	return (1024);
}

static int
fakempu_init(struct snd_midi *kobj, void *cookie)
{
	return (0);
}

static int
fakempu_uninit(struct snd_midi *kobj, void *cookie)
{
	return (0);
}

static void
fakempu_callback(struct snd_midi *kobj, void *cookie, int flags)
{
}

static void
fakempu_callbackp(struct snd_midi *kobj, void *cookie, int flags)
{
}

static const char *
fakempu_descr(struct snd_midi *kobj, void *cookie, int unit)
{
	return ("fake mpu401 UART for DF-4129");
}

static const char *
fakempu_provider(struct snd_midi *kobj)
{
	return ("fakempu");
}

static kobj_method_t fakempu_methods[] = {
	KOBJMETHOD(mpu_inqsize, fakempu_inqsize),
	KOBJMETHOD(mpu_outqsize, fakempu_outqsize),
	KOBJMETHOD(mpu_init, fakempu_init),
	KOBJMETHOD(mpu_uninit, fakempu_uninit),
	KOBJMETHOD(mpu_callback, fakempu_callback),
	KOBJMETHOD(mpu_callbackp, fakempu_callbackp),
	KOBJMETHOD(mpu_descr, fakempu_descr),
	KOBJMETHOD(mpu_provider, fakempu_provider),
	KOBJMETHOD_END
};

DEFINE_CLASS(fakempu, fakempu_methods, 0);

static int
fakempu_modevent(module_t mod, int type, void *data)
{
	int i, ok = 0;

	switch (type) {
	case MOD_LOAD:
		for (i = 0; i < FAKE_UNITS; i++) {
			fake_units[i] = midi_init(&fakempu_class, 0, i + 1,
			    NULL);
			if (fake_units[i] != NULL)
				ok++;
		}
		kprintf("fakempu: %d/%d fake MIDI units registered\n",
		    ok, FAKE_UNITS);
		break;
	case MOD_UNLOAD:
		for (i = 0; i < FAKE_UNITS; i++) {
			if (fake_units[i] != NULL) {
				midi_uninit(fake_units[i]);
				fake_units[i] = NULL;
			}
		}
		break;
	default:
		break;
	}
	return (0);
}

DEV_MODULE(fakempu, fakempu_modevent, NULL);
