/*
 * DF-4970 harness: proves the XE_CARD_TYPE_FLAGS_DINGO (0x4) case in
 * xe_pccard_probe()'s flag-propagation loop is DEAD CODE, so CEM56/REM56
 * "Dingo" combo cards never get scp->dingo set.
 *
 * Everything below the MODEL marker is transcribed verbatim from
 * sys/dev/netif/xe/if_xe_pccard.c (lines 90-127 and 311-323) plus the two
 * loop variants under test (stock `!=` vs fixed `<=`).
 *
 * Build:  cc -O -o dingo_flags dingo_flags.c
 * Run:    ./dingo_flags            (exit 0 = invariants hold, 1 = violation)
 */
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

/* ---------------- verbatim from if_xe_pccard.c ---------------- */
#define XE_CARD_TYPE_FLAGS_NO		0x0
#define XE_CARD_TYPE_FLAGS_CE2		0x1
#define XE_CARD_TYPE_FLAGS_MOHAWK	0x2
#define XE_CARD_TYPE_FLAGS_DINGO	0x4

#define XE_PROD_UMASK		0x11000f
#define XE_PROD_SINGLE_ID1	0x010001
#define XE_PROD_SINGLE_ID2	0x010002
#define XE_PROD_SINGLE_ID3	0x010003
#define XE_PROD_MULTI_ID1	0x110001
#define XE_PROD_MULTI_ID2	0x110002
#define XE_PROD_MULTI_ID3	0x110003
#define XE_PROD_MULTI_ID4	0x110004
#define XE_PROD_MULTI_ID5	0x110005
#define XE_PROD_MULTI_ID6	0x110006
#define XE_PROD_MULTI_ID7	0x110007

struct xe_card_type {
	uint32_t	 prod_type;
	const char	*card_type_desc;
	uint32_t	 flags;
} xe_card_types[] = {
	{ XE_PROD_MULTI_ID1,	"CEM",		XE_CARD_TYPE_FLAGS_NO },
	{ XE_PROD_MULTI_ID2,	"CEM2",		XE_CARD_TYPE_FLAGS_CE2 },
	{ XE_PROD_MULTI_ID3,	"CEM3",		XE_CARD_TYPE_FLAGS_CE2 },
	{ XE_PROD_MULTI_ID4,	"CEM33",	XE_CARD_TYPE_FLAGS_CE2 },
	{ XE_PROD_MULTI_ID5,	"CEM56M",	XE_CARD_TYPE_FLAGS_MOHAWK },
	{ XE_PROD_MULTI_ID6,	"CEM56",	XE_CARD_TYPE_FLAGS_MOHAWK |
						XE_CARD_TYPE_FLAGS_DINGO },
	{ XE_PROD_MULTI_ID7,	"CEM56",	XE_CARD_TYPE_FLAGS_MOHAWK |
						XE_CARD_TYPE_FLAGS_DINGO },
	{ XE_PROD_SINGLE_ID1,	"CE",		XE_CARD_TYPE_FLAGS_NO },
	{ XE_PROD_SINGLE_ID2,	"CE2",		XE_CARD_TYPE_FLAGS_CE2 },
	{ XE_PROD_SINGLE_ID3,	"CE3",		XE_CARD_TYPE_FLAGS_MOHAWK },
	{ 0, NULL, -1 }
};

/* softc fields written by the loop (if_xevar.h) */
struct scp_model {
	int ce2, mohawk, dingo;
};

static struct xe_card_type *
xe_card_type_lookup(uint32_t prod)		/* verbatim */
{
	struct xe_card_type *ct;

	for (ct = xe_card_types; ct->card_type_desc != NULL; ++ct) {
		if(ct->prod_type == (prod & XE_PROD_UMASK))
			return ct;
	}
	return NULL;
}

/* ---------------- the loop under test, two variants ------------- */
static void
probe_flags_stock(struct scp_model *scp, struct xe_card_type *card_itm)
{
	int i;

	scp->ce2 = scp->mohawk = scp->dingo = 0;
	/* stock if_xe_pccard.c:311 */
	for (i = 1; i != XE_CARD_TYPE_FLAGS_DINGO; i = i << 1) {
		switch(i & card_itm->flags) {
		case XE_CARD_TYPE_FLAGS_CE2:	scp->ce2 = 1;	break;
		case XE_CARD_TYPE_FLAGS_MOHAWK:	scp->mohawk = 1; break;
		case XE_CARD_TYPE_FLAGS_DINGO:	scp->dingo = 1;	break;
		}
	}
}

static void
probe_flags_fixed(struct scp_model *scp, struct xe_card_type *card_itm)
{
	int i;

	scp->ce2 = scp->mohawk = scp->dingo = 0;
	/* proposed fix: iterate through the DINGO bit as well */
	for (i = 1; i <= XE_CARD_TYPE_FLAGS_DINGO; i = i << 1) {
		switch(i & card_itm->flags) {
		case XE_CARD_TYPE_FLAGS_CE2:	scp->ce2 = 1;	break;
		case XE_CARD_TYPE_FLAGS_MOHAWK:	scp->mohawk = 1; break;
		case XE_CARD_TYPE_FLAGS_DINGO:	scp->dingo = 1;	break;
		}
	}
}

int
main(void)
{
	static const uint32_t prods[] = {
		XE_PROD_SINGLE_ID1, XE_PROD_SINGLE_ID2, XE_PROD_SINGLE_ID3,
		XE_PROD_MULTI_ID1, XE_PROD_MULTI_ID2, XE_PROD_MULTI_ID3,
		XE_PROD_MULTI_ID4, XE_PROD_MULTI_ID5, XE_PROD_MULTI_ID6,
		XE_PROD_MULTI_ID7,
	};
	int rc = 0, dingo_seen_stock = 0;

	printf("%-10s %-8s %s\n", "prod", "card", "flags (ce2,mohawk,dingo)");
	for (size_t k = 0; k < sizeof(prods)/sizeof(prods[0]); k++) {
		struct xe_card_type *ct = xe_card_type_lookup(prods[k]);
		struct scp_model s, f;

		if (ct == NULL) {
			printf("0x%06x   LOOKUP-FAIL\n", prods[k]);
			rc = 1;
			continue;
		}
		probe_flags_stock(&s, ct);
		probe_flags_fixed(&f, ct);
		dingo_seen_stock |= s.dingo;
		{
			char left[32], right[32];
			snprintf(left, sizeof left, "%d,%d,%d", s.ce2, s.mohawk, s.dingo);
			snprintf(right, sizeof right, "%d,%d,%d", f.ce2, f.mohawk, f.dingo);
			printf("0x%06x   %-8s ce2,mohawk,dingo stock=%-10s fixed=%s\n",
			    prods[k], ct->card_type_desc, left, right);
		}
		/* invariant: entries whose table flags include DINGO (CEM56
		 * MULTI_ID6/ID7) must end with dingo set -- required by
		 * xe_cemfix (if_xe_pccard.c:201-208) and xe_activate's
		 * RealPort aligned-port path (if_xe.c:1915+). */
		if ((ct->flags & XE_CARD_TYPE_FLAGS_DINGO) && !s.dingo) {
			printf("           VIOLATION: %s is a Dingo card but "
			    "scp->dingo == 0 in the STOCK loop\n",
			    ct->card_type_desc);
			rc = 1;
		}
		if ((ct->flags & XE_CARD_TYPE_FLAGS_DINGO) && !f.dingo) {
			printf("           VIOLATION: fix failed for %s\n",
			    ct->card_type_desc);
			rc = 1;
		}
	}

	printf("\nstock loop ever sets dingo: %s\n", dingo_seen_stock ? "yes" : "NO (case is dead code)");
	printf("%s\n", rc == 0 ? "RESULT: invariants hold (dead case confirmed by table)" :
	    "RESULT: VIOLATIONS FOUND");
	return rc;
}
