/*
 * rig2_shims.h -- KERNEL-ENVIRONMENT EMULATION for the verbatim pccard.c
 * lifecycle rig (DF-2318 / DF-5355 / DF-5356).
 *
 *   *** NOT VERBATIM *** -- everything the machine-extracted production code
 *   (pccard_core.inc from sys/bus/pccard/pccard.c,
 *    pccard_cis_core.inc from sys/bus/pccard/pccard_cis.c,
 *    subrbus_rl.inc from sys/kern/subr_bus.c)
 *   needs from the kernel environment, emulated in userland:
 *
 *   - device_t is a fake device object; the device method layer
 *     (device_add_child / device_probe_and_attach / device_get_state ...)
 *     mirrors the production subr_bus.c semantics that matter here:
 *       device_doattach(): attach ok -> devadded(dev) -> bus_child_pnpinfo_str
 *                          (subr_bus.c:2107-2114, :633-645)
 *       probe/attach fail -> devnomatch(dev) -> bus_child_pnpinfo_str
 *                          (subr_bus.c:1984/2039 -> :673)
 *   - bus_alloc_resource() implements a bridge window model:
 *       SYS_RES_IOPORT   window [0x110,0x2FF]; fixed ranges granted iff they
 *                        fit; "anywhere" (start==0) granted at the cursor.
 *                        This is the policy a real pcic/cbb bridge enforces
 *                        (its PCI I/O window is finite; fixed host ranges
 *                        outside it cannot be decoded).
 *       SYS_RES_MEMORY size 4096 -> the CIS attribute/common window
 *                        (swapped by CARD_SET_RES_FLAGS, as in DF-2319).
 *       SYS_RES_MEMORY size 1024 RF_ACTIVE -> the CCR mapping window.
 *   - kmalloc/kfree are canary-banded and ledgered (leak accounting).
 *   - panic() prints "RIG KERNEL PANIC: <msg>" and exits 42 -- the userland
 *     analogue of the kernel's panic (which halts the system).
 */
#ifndef RIG2_SHIMS_H
#define RIG2_SHIMS_H

#include <sys/types.h>
#include <sys/queue.h>	/* byte-identical to kernel sys/sys/queue.h (hash-checked) */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdarg.h>
#include <setjmp.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>

/* ---------------- kernel types ---------------- */
typedef void driver_intr_t_(void);
#define	driver_intr_t	driver_intr_t_

/* resource types (sys/sys/bus_resource.h:41-44, production values) */
#define	SYS_RES_IRQ	1
#define	SYS_RES_DRQ	2
#define	SYS_RES_MEMORY	3
#define	SYS_RES_IOPORT	4

#define	RF_ACTIVE	0x01
#define	RF_SHAREABLE	0x80
#define	rman_make_alignment_flags(x)	0

/* device states (sys/sys/bus.h:76-81) */
enum rig_devstate { DS_NOTPRESENT, DS_ALIVE, DS_ATTACHING, DS_ATTACHED, DS_BUSY };

/* ---------------- debug knobs (owned here; verbatim code externs them) - */
int pccard_debug = 0;
int pccard_cis_debug = 0;
int bootverbose = 0;

#define	kprintf	printf
#define	ksnprintf	snprintf
#define	ksprintf	sprintf
#define	device_printf(d, ...)	printf("pccard0: " __VA_ARGS__)
#ifndef	howmany
#define	howmany(x, y)	(((x)+((y)-1))/(y))
#endif
#define	bzero(p, n)	memset((p), 0, (n))

static void
rig_panic(const char *fmt, ...)
{
	va_list ap;
	va_start(ap, fmt);
	printf("RIG KERNEL PANIC: ");
	vprintf(fmt, ap);
	printf("\n");
	va_end(ap);
	fflush(stdout);
	_exit(42);	/* the kernel would halt here */
}
#define	panic	rig_panic

/* ---------------- allocation ledger ---------------- */
#define	RIG_MAX_BLOCKS	65536
#define	RIG_CANARY	0xC3
#define	M_DEVBUF	0
#define	M_BUS		1
#define	M_NOWAIT	1
#define	M_WAITOK	2
#define	M_INTWAIT	2
#define	M_ZERO		4

struct rig_block {
	void       *p;
	size_t      size;
	int         live;
	const char *site;
};
static struct rig_block rig_blocks[RIG_MAX_BLOCKS];
static int    rig_nblocks;

static void *
rig_kmalloc(size_t sz, int flags, const char *site)
{
	unsigned char *p, *q;
	size_t i;

	if (rig_nblocks >= RIG_MAX_BLOCKS)
		return NULL;
	p = malloc(sz + 16);
	if (p == NULL)
		return NULL;
	for (i = 0; i < 8; i++) p[i] = RIG_CANARY;
	q = p + 8 + sz;
	for (i = 0; i < 8; i++) q[i] = RIG_CANARY;
	if (flags & M_ZERO)
		memset(p + 8, 0, sz);
	rig_blocks[rig_nblocks].p = p + 8;
	rig_blocks[rig_nblocks].size = sz;
	rig_blocks[rig_nblocks].live = 1;
	rig_blocks[rig_nblocks].site = site;
	rig_nblocks++;
	return p + 8;
}
#define	kmalloc(sz, type, flags)	rig_kmalloc((sz), (flags), __func__)

static void
rig_kfree(void *p, const char *site)
{
	int i;
	unsigned char *raw;
	size_t k, sz;

	if (p == NULL)
		return;
	for (i = rig_nblocks - 1; i >= 0; i--)
		if (rig_blocks[i].p == p)
			break;
	if (i < 0) {
		printf("RIG kfree(%p) of UNREGISTERED block (%s)\n", p, site);
		abort();
	}
	if (!rig_blocks[i].live) {
		printf("RIG DOUBLE FREE of %p (%s)\n", p, site);
		abort();
	}
	sz = rig_blocks[i].size;
	raw = (unsigned char *)p - 8;
	for (k = 0; k < 8; k++)
		if (raw[k] != RIG_CANARY || raw[sz + 8 + k] != RIG_CANARY) {
			printf("RIG HEAP SMASH on block %p (%zu bytes, %s)\n",
			    p, sz, rig_blocks[i].site);
			abort();
		}
	rig_blocks[i].live = 0;
	free(raw);
}
#define	kfree(p, type)	rig_kfree((p), __func__)

/* ---------------- fake device tree ---------------- */
struct rig_device {
	struct rig_device *parent;
	struct rig_device *next;	/* global tracking list */
	void		*ivars;
	int		state;
	int		deleted;
	char		nameunit[32];
	char		desc[96];
};
typedef struct rig_device *device_t;

extern struct rig_device rig_bus_dev;	 /* the pccard bus device (pccard0) */
extern struct rig_device rig_bridge_dev; /* pcic/cbb bridge (parent) */
extern struct rig_device *rig_devices;	 /* every child ever added */
extern int rig_nchildren;

/* probe/attach policy for device_probe_and_attach(): how the driver layer
 * answers for pccard function children */
enum rig_probe_mode {
	PROBE_ATTACH_OK,	/* probe+attach succeed -> devadded path */
	PROBE_FAIL,		/* probe fails -> devnomatch path */
	PROBE_SILENT_FAIL	/* fails but suppress the notify (rig-only) */
};
extern int rig_probe_mode;

static __inline device_t
device_add_child(device_t parent, const char *name, int unit)
{
	device_t d = rig_kmalloc(sizeof(struct rig_device), M_ZERO,
	    "device_add_child");
	device_t *tp = &rig_devices;
	(void)name; (void)unit;
	d->parent = parent;
	d->state = DS_NOTPRESENT;
	snprintf(d->nameunit, sizeof(d->nameunit), "pccardfunc%d",
	    rig_nchildren++);
	while (*tp != NULL)
		tp = &(*tp)->next;
	*tp = d;
	return d;
}
static __inline void	device_set_ivars(device_t d, void *iv) { d->ivars = iv; }
static __inline void *	device_get_ivars(device_t d) { return d->ivars; }
static __inline device_t	device_get_parent(device_t d) { return d->parent; }
static __inline int	device_get_state(device_t d) { return d->state; }
static __inline const char *device_get_nameunit(device_t d) { return d->nameunit; }
static __inline void	device_set_desc_copy(device_t d, const char *s)
	{ snprintf(d->desc, sizeof(d->desc), "%s", s); }
static __inline int	device_get_unit(device_t d) { (void)d; return 0; }

/* device_get_softc: only the pccard bus device has a softc here; the storage
 * is defined in rig2_main.c (type known after the verbatim includes). */
extern void *rig_sc_ptr;
static __inline void *	device_get_softc(device_t d) { (void)d; return rig_sc_ptr; }

int bus_child_pnpinfo_str(device_t child, char *buf, size_t buflen);

/* Emulates production device_probe_and_attach() -> device_doattach()
 * (subr_bus.c:2107-2114): on attach success devadded(dev) calls
 * bus_child_pnpinfo_str(); on probe failure devnomatch(dev) does the same
 * (subr_bus.c:1984/2039 -> :673). */
static __inline int
device_probe_and_attach(device_t d)
{
	char buf[1024];

	if (rig_probe_mode == PROBE_ATTACH_OK) {
		d->state = DS_ATTACHED;
		*buf = '\0';
		bus_child_pnpinfo_str(d, buf, sizeof(buf));	/* devadded() */
		printf("    [devadded] %s pnpinfo=\"%s\"\n",
		    d->nameunit, buf);
		return (0);
	}
	if (rig_probe_mode == PROBE_FAIL) {
		*buf = '\0';
		bus_child_pnpinfo_str(d, buf, sizeof(buf));	/* devnomatch() */
	}
	return (ENXIO);
}
static __inline int	device_detach(device_t d)
	{ d->state = DS_NOTPRESENT; return (0); }
static __inline int	device_delete_child(device_t p, device_t d)
	{ (void)p; d->deleted = 1; d->state = DS_NOTPRESENT; return (0); }

/* forward decl: defined VERBATIM in pccard_core.inc (static) */
static int pccard_child_pnpinfo_str(device_t bus, device_t child,
    char *buf, size_t buflen);

/* BUS_CHILD_PNPINFO_STR method dispatch: the pccard bus is the only bus with
 * children in this rig, so the method resolves to the verbatim pccard
 * implementation (production DEVMETHOD table, pccard.c:1340). */
#define	BUS_CHILD_PNPINFO_STR(p, c, b, l)	pccard_child_pnpinfo_str((p), (c), (b), (l))

/* BUS_READ_IVAR dispatch -> verbatim pccard_read_ivar (pccard.c:1339 table);
 * defined in rig2_main.c after the verbatim code is visible. */
int rig_bus_read_ivar(device_t bus, device_t dev, int which, u_char *result);
#define	BUS_READ_IVAR(p, d, w, r)	rig_bus_read_ivar((p), (d), (w), (u_char *)(r))
#define	BUS_TEARDOWN_INTR(d, c, r, ck)	((void)0)	/* intr_handler stays NULL */

/* CARD interface methods (card_if.h) -> bridge shims */
extern long rig_power_enables, rig_power_disables;
extern long rig_flag_switches;
#define	POWER_ENABLE_SOCKET(p, d)	(rig_power_enables++)
#define	POWER_DISABLE_SOCKET(p, d)	(rig_power_disables++)

/* ---------------- rman / resource emulation ---------------- */
typedef struct resource *bus_space_tag_t;
typedef unsigned char  *bus_space_handle_t;
typedef unsigned long   bus_addr_t;
typedef unsigned long   bus_size_t;

struct resource {
	unsigned char *handle;
	size_t         size;
	unsigned long  start, end;
	device_t       owner;
};

#define	RIG_MAPSIZE	4096	/* PCCARD_CIS_SIZE */
#define	RIG_TOTBUF	65536	/* guard: [4096,65536) canary zone */
#define	RIG_CCRBUF	4096

extern unsigned char rig_attr_img[RIG_TOTBUF];
extern unsigned char rig_com_img[RIG_TOTBUF];
extern unsigned char rig_ccr_img[RIG_CCRBUF];

extern struct resource rig_cis_res;
extern struct resource rig_ccr_res;
extern struct resource rig_irq_res;
extern int  rig_space;		/* 0=attribute 1=common */

/* IOPORT window model (what a finite bridge window grants) */
#define	RIG_IO_WIN_START	0x110
#define	RIG_IO_WIN_END		0x2FF
extern unsigned long rig_io_cursor;
extern long rig_io_grants, rig_io_releases;
extern long rig_ccr_allocs, rig_ccr_releases;

#define	RIG_IO_POOL	64
extern struct resource rig_io_pool[RIG_IO_POOL];

struct resource *rig_bus_alloc_io(unsigned long start, unsigned long end,
    unsigned long count);

static __inline struct resource *
bus_alloc_resource(device_t dev, int type, int *rid, bus_addr_t start,
    bus_addr_t end, bus_size_t size, u_int flags)
{
	struct resource *r;

	(void)dev; (void)rid;
	if (type == SYS_RES_MEMORY && size == RIG_MAPSIZE) {
		/* CIS window (pccard_cis.c:130-131) */
		rig_cis_res.handle = rig_attr_img;
		rig_space = 0;
		return &rig_cis_res;
	}
	if (type == SYS_RES_MEMORY && size == (1 << 10) && (flags & RF_ACTIVE)) {
		/* CCR mapping (pccard.c:629-630) */
		rig_ccr_allocs++;
		rig_ccr_res.owner = dev;
		return &rig_ccr_res;
	}
	if (type == SYS_RES_IOPORT) {
		r = rig_bus_alloc_io(start, end, size);
		if (r != NULL)
			r->owner = dev;
		return r;
	}
	return NULL;
}
static __inline struct resource *
bus_alloc_resource_any(device_t dev, int type, int *rid, u_int flags)
{
	(void)dev; (void)rid; (void)flags;
	if (type == SYS_RES_IRQ)
		return &rig_irq_res;
	return NULL;
}
static __inline int
bus_release_resource(device_t dev, int type, int rid, struct resource *res)
{
	int i;
	(void)dev; (void)type; (void)rid;
	if (res == &rig_ccr_res) {
		rig_ccr_releases++;
		return 0;
	}
	if (res == &rig_cis_res || res == &rig_irq_res)
		return 0;
	for (i = 0; i < RIG_IO_POOL; i++)
		if (res == &rig_io_pool[i] && res->size != 0) {
			res->size = 0;
			rig_io_releases++;
			return 0;
		}
	return 0;
}
#define	BUS_RELEASE_RESOURCE(p, d, t, r, res)	bus_release_resource((d), (t), (r), (res))

static __inline bus_space_tag_t	rman_get_bustag(struct resource *r)
	{ return (bus_space_tag_t)r; }
static __inline bus_space_handle_t	rman_get_bushandle(struct resource *r)
	{ return r->handle; }
static __inline unsigned long	rman_get_start(struct resource *r)
	{ return r->start; }
static __inline unsigned long	rman_get_end(struct resource *r)
	{ return r->end; }
static __inline unsigned long	rman_get_size(struct resource *r)
	{ return r->size; }
static __inline device_t	rman_get_device(struct resource *r)
	{ return r->owner; }
static __inline void	rman_set_device(struct resource *r, device_t d)
	{ r->owner = d; }
static __inline int	rman_get_cpuid(struct resource *r)
	{ (void)r; return 0; }

/* CARD_SET_RES_FLAGS: swap the active CIS image (attr/common).  The CCR
 * window call (pccard.c:636-637) is a no-op for the rig's static window. */
static __inline int
rig_CARD_SET_RES_FLAGS(device_t p, device_t d, int t, int r, int flags)
{
	(void)p; (void)d; (void)t; (void)r;
	rig_flag_switches++;
	rig_space = (flags == 0);		/* 0 == PCCARD_A_MEM_COM */
	rig_cis_res.handle = rig_space ? rig_com_img : rig_attr_img;
	return 0;
}
#define	CARD_SET_RES_FLAGS(p, d, t, r, f) \
	rig_CARD_SET_RES_FLAGS((p), (d), (t), (r), (f))

static __inline int
rig_CARD_SET_MEMORY_OFFSET(device_t p, device_t d, int r, uint32_t off,
    uint32_t *deltap)
{
	(void)p; (void)d; (void)r;
	*deltap = off & (RIG_CCRBUF - 1);
	return 0;
}
#define	CARD_SET_MEMORY_OFFSET(p, d, r, o, dp) \
	rig_CARD_SET_MEMORY_OFFSET((p), (d), (r), (o), (dp))

/* ---------------- bus space ---------------- */
static __inline uint8_t
bus_space_read_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t off)
{
	(void)t;
	if (h == rig_ccr_img) {
		if (off >= RIG_CCRBUF) {
			printf("RIG CCR READ OOB off=%lu\n", (unsigned long)off);
			abort();
		}
		return h[off];
	}
	if (off >= RIG_TOTBUF)
		return 0xFF;		/* wild: master-abort analogue */
	return h[off];
}
static __inline void
bus_space_write_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t off,
    uint8_t v)
{
	(void)t;
	if (h == rig_ccr_img) {
		if (off >= RIG_CCRBUF) {
			printf("RIG CCR WRITE OOB off=%lu\n", (unsigned long)off);
			abort();
		}
		h[off] = v;
		return;
	}
	printf("RIG UNEXPECTED bus_space_write_1 off=%lu v=%#x (dropped)\n",
	    (unsigned long)off, v);
}

#endif /* RIG2_SHIMS_H */
