/* DF-4549 — host-side demonstration of the viapm_wait() off-by-one.
 *
 * Replicates the EXACT control flow of sys/dev/powermng/viapm/viapm.c:567-605
 * with the hardware register read replaced by a fixture, to show the two
 * failure modes of the `while (count--)` / `if (!count)` pair:
 *
 *   (A) controller never completes (perpetual SMBHST_BUSY): after the loop
 *       exhausts, count == -1, so `!count` is FALSE -> SMB_ETIMEOUT is NEVER
 *       set -> viasmb_recvb/readb/readw/bread return SMB_ENOERR ("success")
 *       with stale SMBHDATA0/1 register contents as data.
 *
 *   (B) completion lands exactly on the 10000th (final) poll: break leaves
 *       count == 0 -> `!count` is TRUE -> a SUCCESSFUL transaction is
 *       spuriously failed with SMB_ETIMEOUT.
 */
#include <stdio.h>

#define SMB_ENOERR    0x0
#define SMB_ETIMEOUT  0x20
#define SMBHST_BUSY   0x01
#define SMBHST_INTR   0x02

static int fake_smbhst;             /* fixture: SMBHST register value */

static int
viapm_wait_fixed_point(int fake_STS_register_on_every_poll)
{
	int count = 10000;
	int sts = 0;
	int error;

	fake_smbhst = fake_STS_register_on_every_poll;

	/* --- verbatim loop shape from viapm.c:574-581 --- */
	while (count--) {
		/* DELAY(10); VIAPM_INB(SMBHST) */
		sts = fake_smbhst;
		if (!(sts & SMBHST_BUSY) && (sts & SMBHST_INTR))
			break;
	}

	error = SMB_ENOERR;

	/* --- verbatim timeout test from viapm.c:587-588 --- */
	if (!count)
		error |= SMB_ETIMEOUT;

	return error;
}

int
main(void)
{
	int r;

	/* Case A: wedged bus — BUSY forever. Expected correct: SMB_ETIMEOUT. */
	r = viapm_wait_fixed_point(SMBHST_BUSY);
	printf("case A (perpetual BUSY, real timeout): error=0x%x %s\n", r,
	    (r & SMB_ETIMEOUT) ? "-> timeout correctly reported"
			       : "-> *** SMB_ETIMEOUT MISSING: kernel reports SUCCESS on wedged bus ***");

	/* Case B: completion on the very last poll iteration. */
	fake_smbhst = 0;	/* patched below for last-iteration only */
	{
		int count = 10000;
		int sts = 0;
		int error = SMB_ENOERR;
		while (count--) {
			sts = (count == 0) ? SMBHST_INTR /* last poll: done */
					   : SMBHST_BUSY;
			if (!(sts & SMBHST_BUSY) && (sts & SMBHST_INTR))
				break;
		}
		if (!count)
			error |= SMB_ETIMEOUT;
		printf("case B (INTR on final poll, success):   error=0x%x %s\n",
		    error,
		    (error & SMB_ETIMEOUT)
			? "-> *** SPURIOUS SMB_ETIMEOUT on successful transaction ***"
			: "-> success correctly reported");
	}
	return 0;
}
