/*
 * DF-5488 KLD harness -- verbatim-production demonstration of the
 * uvisor_attach() -> uvisor_init() synchronous-EP0 explore-thread stall.
 * Same evidence class as the proven family findings DF-5407 (uplcom,
 * 14 x 5 s = ~70 s) and DF-5359 (uipaq, ~12.8 s): the production
 * uvisor_init() is awk/sed-extracted VERBATIM from the guest's clean
 * /usr/src/sys/bus/u4b/serial/uvisor.c (see build.sh, sha256-recorded)
 * and only the two EP0 entry points it calls are re-targeted to an
 * evil-device model.
 *
 * WHAT IT PROVES:
 *
 * uvisor_attach() (uvisor.c:302-354) runs on the per-bus USB explore
 * thread and calls uvisor_init() (uvisor.c:326 -> 370-517) which issues
 * synchronous control requests at the USB_DEFAULT_TIMEOUT of 5000 ms
 * (usbdi.h:87):
 *
 *   UVISOR_FLAG_VISOR  (HANDSPRING VISOR 082d:0100, uvisor.c:254):
 *     1 x UVISOR_GET_CONNECTION_INFORMATION vendor control-IN
 *     (uvisor.c:382-389)                       => ~5 s
 *   UVISOR_FLAG_PALM4  (Treo, Palm m-series, Clie, Zire; uvisor.c:250-277):
 *     1 x UVISOR_GET_PALM_INFORMATION vendor control-IN
 *     (uvisor.c:434-442)                       => ~5 s
 *   UVISOR_FLAG_PALM35 (SONY CLIE_35, uvisor.c:272):
 *     2 x standard requests (UR_GET_CONFIG + UR_GET_INTERFACE)
 *     (uvisor.c:476-485, 488-496)              => ~10 s
 *
 * USB permits a device to NAK a control data stage for the whole 5 s
 * timeout; an evil adapter answering each request just-under-timeout
 * (or never answering) pins the explore thread -- which serializes all
 * device enumeration on the bus -- for the full chain, while every
 * request additionally holds the per-device enum lock
 * (usb_request.c:467).
 *
 * PHASE A (evil, in-spec slow answers): each request "succeeds" after
 *   ~4.9 s of NAK, so the chain marches on: VISOR ~4.9 s, PALM4 ~4.9 s,
 *   PALM35 ~9.8 s.
 * PHASE B (fix mechanism from fix.diff: bound the attach-path control
 *   timeout to 250 ms): the still-NAKing device times out at 250 ms and
 *   uvisor_init() aborts on the first error (uvisor.c:391-393/444-446/
 *   483-485/494-496): all variants ~0.25 s.
 */
#include <sys/stdint.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/lock.h>
#include <sys/condvar.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/malloc.h>

#include <sys/bus.h>
#include <bus/u4b/usb.h>
#include <bus/u4b/usbdi.h>
#include <bus/u4b/usbdi_util.h>
#include <bus/u4b/usb_process.h>	/* struct usb_process (sc_tq) */
#include <bus/u4b/serial/usb_serial.h>	/* struct ucom_* inside softc */

/* production uvisor.c does this before usb_debug.h (DPRINTF no-op in a
 * non-USB_DEBUG build, exactly like the shipped kernel module) */
#define	USB_DEBUG_VAR hx_uvisor_debug
#include <bus/u4b/usb_debug.h>

#ifndef	UVISOR_INIT_TIMEOUT
#define	UVISOR_INIT_TIMEOUT	250	/* ms (fix.diff) */
#endif

#include "uv_defs.h"		/* VERBATIM uvisor.c:85-182 (defines/structs) */
#include "uv_init.h"		/* VERBATIM uvisor_init(), uvisor.c:370-517 */
#include "uv_init_fixed.h"	/* fix.diff'd uvisor_init() (renamed), phase B */

static int hx_evil_ms = 4900;	/* evil device: NAK ~4.9 s, then answer */
static int hx_fixed;		/* phase B: fix mechanism in effect */

static int64_t
hx_ms(struct timeval *t0, struct timeval *t1)
{
	return ((int64_t)(t1->tv_sec - t0->tv_sec) * 1000 +
	    (t1->tv_usec - t0->tv_usec) / 1000);
}

/*
 * Evil-device model standing in for the kernel's real
 * usbd_do_request_flags() (usb_request.c:565+): the device NAKs the data
 * stage for ~4.9 s (USB-legal, under the 5 s timeout), then either
 * answers successfully (phase A: the chain continues, worst case) or is
 * cut off by the driver-side 250 ms bound (phase B: the request times
 * out, uvisor_init aborts).
 */
usb_error_t
usbd_do_request_flags(struct usb_device *udev, struct lock *lock,
    struct usb_device_request *req, void *data, uint16_t flags,
    uint16_t *actlen, usb_timeout_t timeout)
{
	uint16_t len = UGETW(req->wLength);
	int delay;
	usb_error_t err;

	delay = (int)timeout;
	if (delay > hx_evil_ms)
		delay = hx_evil_ms;

	kprintf("HX5488:   EP0 bmRequestType=0x%02x bRequest=0x%02x "
	    "wValue=%d wIndex=%d wLength=%d: device NAKs %d ms then %s\n",
	    req->bmRequestType, req->bRequest, UGETW(req->wValue),
	    UGETW(req->wIndex), len, delay,
	    delay < (int)timeout ? "answers (success)" :
	    "is cut off (driver timeout, chain aborts)");

	/* the "WXFER" wait the real requester does (usb_request.c:642-644) */
	if (delay > 0)
		tsleep(&hx_evil_ms, 0, "EVILNAK", USB_MS_TO_TICKS(delay));

	if (delay < (int)timeout) {
		/* in-spec slow answer: benign bytes, full actlen */
		if (data != NULL && len != 0)
			memset(data, 0x5A, len);
		if (actlen != NULL)
			*actlen = len;
		err = USB_ERR_NORMAL_COMPLETION;
	} else {
		/* fix mechanism: the bounded timeout fires first */
		err = USB_ERR_TIMEOUT;
	}
	kprintf("HX5488:   EP0 done -> %s\n", usbd_errstr(err));
	return (err);
}

static void
hx_run(const char *name, uint16_t flag)
{
	struct uvisor_softc sc;
	struct usb_config cfg[UVISOR_N_TRANSFER];
	struct timeval t0, t1;
	usb_error_t err;

	memset(&sc, 0, sizeof(sc));
	memset(cfg, 0, sizeof(cfg));
	sc.sc_flag = flag;

	kprintf("HX5488: %s (sc_flag=0x%04x): uvisor_init() chain START\n",
	    name, flag);
	microtime(&t0);
	err = uvisor_init(&sc, NULL, cfg);
	microtime(&t1);
	kprintf("HX5488: %s: uvisor_init() DONE err=%s ELAPSED=%jd ms"
	    " (cfg endpoints %d/%d)\n",
	    name, usbd_errstr(err), (intmax_t)hx_ms(&t0, &t1),
	    cfg[0].endpoint, cfg[1].endpoint);
}

static void
hx_run_fixed(const char *name, uint16_t flag)
{
	struct uvisor_softc sc;
	struct usb_config cfg[UVISOR_N_TRANSFER];
	struct timeval t0, t1;
	usb_error_t err;

	memset(&sc, 0, sizeof(sc));
	memset(cfg, 0, sizeof(cfg));
	sc.sc_flag = flag;

	kprintf("HX5488: %s [fixed driver] (sc_flag=0x%04x): uvisor_init() "
	    "chain START (bounded %d ms timeout, abort on error, hx_fixed=%d)\n",
	    name, flag, UVISOR_INIT_TIMEOUT, hx_fixed);
	microtime(&t0);
	err = uvisor_init_fixed(&sc, NULL, cfg);
	microtime(&t1);
	kprintf("HX5488: %s [fixed driver]: uvisor_init() DONE err=%s "
	    "ELAPSED=%jd ms\n",
	    name, usbd_errstr(err), (intmax_t)hx_ms(&t0, &t1));
}

static int
hx_modevent(module_t mod, int type, void *data)
{
	int error = 0;

	switch (type) {
	case MOD_LOAD:
		kprintf("HX5488: DF-5488 uvisor attach-chain stall harness "
		    "(verbatim uvisor_init), hz=%d, evil NAK=%d ms, "
		    "USB_DEFAULT_TIMEOUT=%d ms\n",
		    hz, hx_evil_ms, USB_DEFAULT_TIMEOUT);

		/* PHASE A: production code, evil in-spec-slow device */
		hx_fixed = 0;
		hx_run("VISOR  (Handspring Visor)", UVISOR_FLAG_VISOR);
		hx_run("PALM4  (Treo/Palm/Clie)", UVISOR_FLAG_PALM4);
		hx_run("PALM35 (Sony Clie 3.5)", UVISOR_FLAG_PALM35);

		/* PHASE B: fix mechanism (250 ms bound, abort on error) */
		hx_fixed = 1;
		hx_run_fixed("VISOR ", UVISOR_FLAG_VISOR);
		hx_run_fixed("PALM4 ", UVISOR_FLAG_PALM4);
		hx_run_fixed("PALM35", UVISOR_FLAG_PALM35);

		kprintf("HX5488: harness complete\n");
		break;
	case MOD_UNLOAD:
		break;
	default:
		error = EOPNOTSUPP;
		break;
	}
	return (error);
}

static moduledata_t hx_mod = { "uvisorstall", hx_modevent, 0 };
DECLARE_MODULE(uvisorstall, hx_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
MODULE_DEPEND(uvisorstall, usb, 1, 1, 1);
