/*
 * fw3443.ko - PoC-only inspector module for DF-3443.
 * Exposes:
 *   hw.fw3443.addr  - kernel address to inspect (writable)
 *   hw.fw3443.dump  - 64 bytes of hex at that address (readable)
 * and prints struct geometry at load.
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/sysctl.h>
#include <sys/systm.h>
#include <sys/malloc.h>

#include <net/if_var.h>
#include <net/altq/altq.h>
#include <net/altq/if_altq.h>
#include <net/altq/altq_priq.h>

static struct sysctl_ctx_list fw_ctx;
static struct sysctl_oid *fw_root;
static unsigned long fw_addr;
static char fw_dump[600];

static int
fw_sysctl_dump(SYSCTL_HANDLER_ARGS)
{
	uint64_t v[8];
	int i, len;

	if (fw_addr == 0)
		return (EINVAL);
	memcpy(v, (void *)(uintptr_t)fw_addr, sizeof(v));
	len = 0;
	fw_dump[0] = '\0';
	for (i = 0; i < 8; i++) {
		len += ksnprintf(fw_dump + len, sizeof(fw_dump) - len,
		    "%016llx%s", (unsigned long long)v[i],
		    (i == 7) ? "" : " ");
	}
	return (sysctl_handle_string(oidp, fw_dump, sizeof(fw_dump), req));
}

static int
fw_sysctl_addr(SYSCTL_HANDLER_ARGS)
{
	return (sysctl_handle_long(oidp, &fw_addr, 0, req));
}

static int
fw_modevent(module_t mod, int type, void *data)
{
	switch (type) {
	case MOD_LOAD:
		sysctl_ctx_init(&fw_ctx);
		fw_root = SYSCTL_ADD_NODE(&fw_ctx,
		    SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, "fw3443",
		    CTLFLAG_RW, 0, "fw3443 inspector");
		if (fw_root == NULL)
			return (ENOMEM);
		SYSCTL_ADD_PROC(&fw_ctx, SYSCTL_CHILDREN(fw_root), OID_AUTO,
		    "addr", CTLTYPE_LONG | CTLFLAG_RW, 0, 0,
		    fw_sysctl_addr, "L", "kernel address to inspect");
		SYSCTL_ADD_PROC(&fw_ctx, SYSCTL_CHILDREN(fw_root), OID_AUTO,
		    "dump", CTLTYPE_STRING | CTLFLAG_RD, 0, 0,
		    fw_sysctl_dump, "A", "64 bytes at addr, hex");
		kprintf("fw3443: sizeof(struct ifnet)=%zu "
		    "offsetof(ifnet.if_snd)=%zu "
		    "offsetof(ifaltq.altq_subq)=%zu "
		    "sizeof(struct ifaltq_subque)=%zu "
		    "offsetof(priq_if.pif_ifq)=%zu "
		    "offsetof(priq_if.pif_classes)=%zu\n",
		    sizeof(struct ifnet),
		    offsetof(struct ifnet, if_snd),
		    offsetof(struct ifaltq, altq_subq),
		    sizeof(struct ifaltq_subque),
		    offsetof(struct priq_if, pif_ifq),
		    offsetof(struct priq_if, pif_classes));
		return (0);
	case MOD_UNLOAD:
		if (sysctl_ctx_free(&fw_ctx) != 0)
			return (ENOTEMPTY);
		return (0);
	default:
		return (EOPNOTSUPP);
	}
}

static moduledata_t fw3443_mod = { "fw3443", fw_modevent, 0 };
DECLARE_MODULE(fw3443, fw3443_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
