static int verify_not_halted(struct usbtest_dev *tdev, int ep, struct urb *urb)
{
	int	retval;
	u16	status;

	/* shouldn't look or act halted */
	retval = usb_get_std_status(urb->dev, USB_RECIP_ENDPOINT, ep, &status);
	if (retval < 0) {
		ERROR(tdev, "ep %02x couldn't get no-halt status, %d\n",
				ep, retval);
		return retval;
	}
	if (status != 0) {
		ERROR(tdev, "ep %02x bogus status: %04x != 0\n", ep, status);
		return -EINVAL;
	}
	retval = simple_io(tdev, urb, 1, 0, 0, __func__);
	if (retval != 0)
		return -EINVAL;
	return 0;
}

static int override_alt = -1;

static void complicated_callback(struct urb *urb);

struct usbtest_param_32 {
	/* inputs */
	__u32		test_num;	/* 0..(TEST_CASES-1) */
	__u32		iterations;
	__u32		length;
	__u32		vary;
	__u32		sglen;

	/* outputs */
	__s32		duration_sec;
	__s32		duration_usec;
}

struct usbtest_param_64 {
	/* inputs */
	__u32		test_num;	/* 0..(TEST_CASES-1) */
	__u32		iterations;
	__u32		length;
	__u32		vary;
	__u32		sglen;

	/* outputs */
	__s64		duration_sec;
	__s64		duration_usec;
}

#define USBTEST_REQUEST_32    _IOWR('U', 100, struct usbtest_param_32)

#define USBTEST_REQUEST_64    _IOWR('U', 100, struct usbtest_param_64)

struct usbtest_info {
	const char		*name;
	u8			ep_in;		/* bulk/intr source */
	u8			ep_out;		/* bulk/intr sink */
	unsigned		autoconf:1;
	unsigned		ctrl_out:1;
	unsigned		iso:1;		/* try iso in/out */
	unsigned		intr:1;		/* try interrupt in/out */
	int			alt;
}

struct usbtest_dev {
	struct usb_interface	*intf;
	struct usbtest_info	*info;
	int			in_pipe;
	int			out_pipe;
	int			in_iso_pipe;
	int			out_iso_pipe;
	int			in_int_pipe;
	int			out_int_pipe;
	struct usb_endpoint_descriptor	*iso_in, *iso_out;
	struct usb_endpoint_descriptor	*int_in, *int_out;
	struct mutex		lock;

#define TBUF_SIZE	256
	u8			*buf;
}

#define ERROR(tdev, fmt, args...) \
	dev_err(&(tdev)->intf->dev , fmt , ## args)

#define WARNING(tdev, fmt, args...) \
	dev_warn(&(tdev)->intf->dev , fmt , ## args)

static unsigned pattern;

static unsigned mod_pattern;

struct sg_timeout {
	struct timer_list timer;
	struct usb_sg_request *req;
}

static unsigned realworld = 1;

struct ctrl_ctx {
	spinlock_t		lock;
	struct usbtest_dev	*dev;
	struct completion	complete;
	unsigned		count;
	unsigned		pending;
	int			status;
	struct urb		**urb;
	struct usbtest_param_32	*param;
	int			last;
}

struct subcase {
	struct usb_ctrlrequest	setup;
	int			number;
	int			expected;
}

struct queued_ctx {
	struct completion	complete;
	atomic_t		pending;
	unsigned		num;
	int			status;
	struct urb		**urbs;
}

struct transfer_context {
	unsigned		count;
	unsigned		pending;
	spinlock_t		lock;
	struct completion	done;
	int			submit_error;
	unsigned long		errors;
	unsigned long		packet_count;
	struct usbtest_dev	*dev;
	bool			is_iso;
}

static unsigned force_interrupt;

static unsigned short vendor;

static unsigned short product;

static struct usbtest_info ez1_info = {
	.name		= "EZ-USB device",
	.ep_in		= 2,
	.ep_out		= 2,
	.alt		= 1,
};


static void usbtest_disconnect(struct usb_interface *intf)
{
	struct usbtest_dev	*dev = usb_get_intfdata(intf);

	usb_set_intfdata(intf, NULL);
	dev_dbg(&intf->dev, "disconnect\n");
	kfree(dev->buf);
	kfree(dev);
}

usb: usbtest: fix missing kfree(dev->buf) in usbtest_disconnect

BUG: memory leak
unreferenced object 0xffff888055046e00 (size 256):
  comm "kworker/2:9", pid 2570, jiffies 4294942129 (age 1095.500s)
  hex dump (first 32 bytes):
    00 70 04 55 80 88 ff ff 18 bb 5a 81 ff ff ff ff  .p.U......Z.....
    f5 96 78 81 ff ff ff ff 37 de 8e 81 ff ff ff ff  ..x.....7.......
  backtrace:
    [<00000000d121dccf>] kmemleak_alloc_recursive
include/linux/kmemleak.h:43 [inline]
    [<00000000d121dccf>] slab_post_alloc_hook mm/slab.h:586 [inline]
    [<00000000d121dccf>] slab_alloc_node mm/slub.c:2786 [inline]
    [<00000000d121dccf>] slab_alloc mm/slub.c:2794 [inline]
    [<00000000d121dccf>] kmem_cache_alloc_trace+0x15e/0x2d0 mm/slub.c:2811
    [<000000005c3c3381>] kmalloc include/linux/slab.h:555 [inline]
    [<000000005c3c3381>] usbtest_probe+0x286/0x19d0
drivers/usb/misc/usbtest.c:2790
    [<000000001cec6910>] usb_probe_interface+0x2bd/0x870
drivers/usb/core/driver.c:361
    [<000000007806c118>] really_probe+0x48d/0x8f0 drivers/base/dd.c:551
    [<00000000a3308c3e>] driver_probe_device+0xfc/0x2a0 drivers/base/dd.c:724
    [<000000003ef66004>] __device_attach_driver+0x1b6/0x240
drivers/base/dd.c:831
    [<00000000eee53e97>] bus_for_each_drv+0x14e/0x1e0 drivers/base/bus.c:431
    [<00000000bb0648d0>] __device_attach+0x1f9/0x350 drivers/base/dd.c:897
    [<00000000838b324a>] device_initial_probe+0x1a/0x20 drivers/base/dd.c:944
    [<0000000030d501c1>] bus_probe_device+0x1e1/0x280 drivers/base/bus.c:491
    [<000000005bd7adef>] device_add+0x131d/0x1c40 drivers/base/core.c:2504
    [<00000000a0937814>] usb_set_configuration+0xe84/0x1ab0
drivers/usb/core/message.c:2030
    [<00000000e3934741>] generic_probe+0x6a/0xe0 drivers/usb/core/generic.c:210
    [<0000000098ade0f1>] usb_probe_device+0x90/0xd0
drivers/usb/core/driver.c:266
    [<000000007806c118>] really_probe+0x48d/0x8f0 drivers/base/dd.c:551
    [<00000000a3308c3e>] driver_probe_device+0xfc/0x2a0 drivers/base/dd.c:724

Acked-by: Alan Stern <stern@rowland.harvard.edu>
Reported-by: Kyungtae Kim <kt0755@gmail.com>
Signed-off-by: Zqiang <qiang.zhang@windriver.com>
Link: https://lore.kernel.org/r/20200612035210.20494-1-qiang.zhang@windriver.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>


test_ctrl_queue(struct usbtest_dev *dev, struct usbtest_param_32 *param)
{
	struct usb_device	*udev = testdev_to_usbdev(dev);
	struct urb		**urb;
	struct ctrl_ctx		context;
	int			i;

	if (param->sglen == 0 || param->iterations > UINT_MAX / param->sglen)
		return -EOPNOTSUPP;

	spin_lock_init(&context.lock);
	context.dev = dev;
	init_completion(&context.complete);
	context.count = param->sglen * param->iterations;
	context.pending = 0;
	context.status = -ENOMEM;
	context.param = param;
	context.last = -1;

	/* allocate and init the urbs we'll queue.
	 * as with bulk/intr sglists, sglen is the queue depth; it also
	 * controls which subtests run (more tests than sglen) or rerun.
	 */
	urb = kcalloc(param->sglen, sizeof(struct urb *), GFP_KERNEL);
	if (!urb)
		return -ENOMEM;
	for (i = 0; i < param->sglen; i++) {
		int			pipe = usb_rcvctrlpipe(udev, 0);
		unsigned		len;
		struct urb		*u;
		struct usb_ctrlrequest	req;
		struct subcase		*reqp;

		/* sign of this variable means:
		 *  -: tested code must return this (negative) error code
		 *  +: tested code may return this (negative too) error code
		 */
		int			expected = 0;

		/* requests here are mostly expected to succeed on any
		 * device, but some are chosen to trigger protocol stalls
		 * or short reads.
		 */
		memset(&req, 0, sizeof(req));
		req.bRequest = USB_REQ_GET_DESCRIPTOR;
		req.bRequestType = USB_DIR_IN|USB_RECIP_DEVICE;

		switch (i % NUM_SUBCASES) {
		case 0:		/* get device descriptor */
			req.wValue = cpu_to_le16(USB_DT_DEVICE << 8);
			len = sizeof(struct usb_device_descriptor);
			break;
		case 1:		/* get first config descriptor (only) */
			req.wValue = cpu_to_le16((USB_DT_CONFIG << 8) | 0);
			len = sizeof(struct usb_config_descriptor);
			break;
		case 2:		/* get altsetting (OFTEN STALLS) */
			req.bRequest = USB_REQ_GET_INTERFACE;
			req.bRequestType = USB_DIR_IN|USB_RECIP_INTERFACE;
			/* index = 0 means first interface */
			len = 1;
			expected = EPIPE;
			break;
		case 3:		/* get interface status */
			req.bRequest = USB_REQ_GET_STATUS;
			req.bRequestType = USB_DIR_IN|USB_RECIP_INTERFACE;
			/* interface 0 */
			len = 2;
			break;
		case 4:		/* get device status */
			req.bRequest = USB_REQ_GET_STATUS;
			req.bRequestType = USB_DIR_IN|USB_RECIP_DEVICE;
			len = 2;
			break;
		case 5:		/* get device qualifier (MAY STALL) */
			req.wValue = cpu_to_le16 (USB_DT_DEVICE_QUALIFIER << 8);
			len = sizeof(struct usb_qualifier_descriptor);
			if (udev->speed != USB_SPEED_HIGH)
				expected = EPIPE;
			break;
		case 6:		/* get first config descriptor, plus interface */
			req.wValue = cpu_to_le16((USB_DT_CONFIG << 8) | 0);
			len = sizeof(struct usb_config_descriptor);
			len += sizeof(struct usb_interface_descriptor);
			break;
		case 7:		/* get interface descriptor (ALWAYS STALLS) */
			req.wValue = cpu_to_le16 (USB_DT_INTERFACE << 8);
			/* interface == 0 */
			len = sizeof(struct usb_interface_descriptor);
			expected = -EPIPE;
			break;
		/* NOTE: two consecutive stalls in the queue here.
		 *  that tests fault recovery a bit more aggressively. */
		case 8:		/* clear endpoint halt (MAY STALL) */
			req.bRequest = USB_REQ_CLEAR_FEATURE;
			req.bRequestType = USB_RECIP_ENDPOINT;
			/* wValue 0 == ep halt */
			/* wIndex 0 == ep0 (shouldn't halt!) */
			len = 0;
			pipe = usb_sndctrlpipe(udev, 0);
			expected = EPIPE;
			break;
		case 9:		/* get endpoint status */
			req.bRequest = USB_REQ_GET_STATUS;
			req.bRequestType = USB_DIR_IN|USB_RECIP_ENDPOINT;
			/* endpoint 0 */
			len = 2;
			break;
		case 10:	/* trigger short read (EREMOTEIO) */
			req.wValue = cpu_to_le16((USB_DT_CONFIG << 8) | 0);
			len = 1024;
			expected = -EREMOTEIO;
			break;
		/* NOTE: two consecutive _different_ faults in the queue. */
		case 11:	/* get endpoint descriptor (ALWAYS STALLS) */
			req.wValue = cpu_to_le16(USB_DT_ENDPOINT << 8);
			/* endpoint == 0 */
			len = sizeof(struct usb_interface_descriptor);
			expected = EPIPE;
			break;
		/* NOTE: sometimes even a third fault in the queue! */
		case 12:	/* get string 0 descriptor (MAY STALL) */
			req.wValue = cpu_to_le16(USB_DT_STRING << 8);
			/* string == 0, for language IDs */
			len = sizeof(struct usb_interface_descriptor);
			/* may succeed when > 4 languages */
			expected = EREMOTEIO;	/* or EPIPE, if no strings */
			break;
		case 13:	/* short read, resembling case 10 */
			req.wValue = cpu_to_le16((USB_DT_CONFIG << 8) | 0);
			/* last data packet "should" be DATA1, not DATA0 */
			if (udev->speed == USB_SPEED_SUPER)
				len = 1024 - 512;
			else
				len = 1024 - udev->descriptor.bMaxPacketSize0;
			expected = -EREMOTEIO;
			break;
		case 14:	/* short read; try to fill the last packet */
			req.wValue = cpu_to_le16((USB_DT_DEVICE << 8) | 0);
			/* device descriptor size == 18 bytes */
			len = udev->descriptor.bMaxPacketSize0;
			if (udev->speed == USB_SPEED_SUPER)
				len = 512;
			switch (len) {
			case 8:
				len = 24;
				break;
			case 16:
				len = 32;
				break;
			}
			expected = -EREMOTEIO;
			break;
		case 15:
			req.wValue = cpu_to_le16(USB_DT_BOS << 8);
			if (udev->bos)
				len = le16_to_cpu(udev->bos->desc->wTotalLength);
			else
				len = sizeof(struct usb_bos_descriptor);
			if (le16_to_cpu(udev->descriptor.bcdUSB) < 0x0201)
				expected = -EPIPE;
			break;
		default:
			ERROR(dev, "bogus number of ctrl queue testcases!\n");
			context.status = -EINVAL;
			goto cleanup;
		}
		req.wLength = cpu_to_le16(len);
		urb[i] = u = simple_alloc_urb(udev, pipe, len, 0);
		if (!u)
			goto cleanup;

		reqp = kmalloc(sizeof(*reqp), GFP_KERNEL);
		if (!reqp)
			goto cleanup;
		reqp->setup = req;
		reqp->number = i % NUM_SUBCASES;
		reqp->expected = expected;
		u->setup_packet = (char *) &reqp->setup;

		u->context = &context;
		u->complete = ctrl_complete;
	}

	/* queue the urbs */
	context.urb = urb;
	spin_lock_irq(&context.lock);
	for (i = 0; i < param->sglen; i++) {
		context.status = usb_submit_urb(urb[i], GFP_ATOMIC);
		if (context.status != 0) {
			ERROR(dev, "can't submit urb[%d], status %d\n",
					i, context.status);
			context.count = context.pending;
			break;
		}
		context.pending++;
	}
	spin_unlock_irq(&context.lock);

	/* FIXME  set timer and time out; provide a disconnect hook */

	/* wait for the last one to complete */
	if (context.pending > 0)
		wait_for_completion(&context.complete);

cleanup:
	for (i = 0; i < param->sglen; i++) {
		if (!urb[i])
			continue;
		urb[i]->dev = udev;
		kfree(urb[i]->setup_packet);
		simple_free_urb(urb[i]);
	}
	kfree(urb);
	return context.status;
}


usbtest_do_ioctl(struct usb_interface *intf, struct usbtest_param_32 *param)
{
	struct usbtest_dev	*dev = usb_get_intfdata(intf);
	struct usb_device	*udev = testdev_to_usbdev(dev);
	struct urb		*urb;
	struct scatterlist	*sg;
	struct usb_sg_request	req;
	unsigned		i;
	int	retval = -EOPNOTSUPP;

	if (param->iterations <= 0)
		return -EINVAL;
	if (param->sglen > MAX_SGLEN)
		return -EINVAL;
	/*
	 * Just a bunch of test cases that every HCD is expected to handle.
	 *
	 * Some may need specific firmware, though it'd be good to have
	 * one firmware image to handle all the test cases.
	 *
	 * FIXME add more tests!  cancel requests, verify the data, control
	 * queueing, concurrent read+write threads, and so on.
	 */
	switch (param->test_num) {

	case 0:
		dev_info(&intf->dev, "TEST 0:  NOP\n");
		retval = 0;
		break;

	/* Simple non-queued bulk I/O tests */
	case 1:
		if (dev->out_pipe == 0)
			break;
		dev_info(&intf->dev,
				"TEST 1:  write %d bytes %u times\n",
				param->length, param->iterations);
		urb = simple_alloc_urb(udev, dev->out_pipe, param->length, 0);
		if (!urb) {
			retval = -ENOMEM;
			break;
		}
		/* FIRMWARE:  bulk sink (maybe accepts short writes) */
		retval = simple_io(dev, urb, param->iterations, 0, 0, "test1");
		simple_free_urb(urb);
		break;
	case 2:
		if (dev->in_pipe == 0)
			break;
		dev_info(&intf->dev,
				"TEST 2:  read %d bytes %u times\n",
				param->length, param->iterations);
		urb = simple_alloc_urb(udev, dev->in_pipe, param->length, 0);
		if (!urb) {
			retval = -ENOMEM;
			break;
		}
		/* FIRMWARE:  bulk source (maybe generates short writes) */
		retval = simple_io(dev, urb, param->iterations, 0, 0, "test2");
		simple_free_urb(urb);
		break;
	case 3:
		if (dev->out_pipe == 0 || param->vary == 0)
			break;
		dev_info(&intf->dev,
				"TEST 3:  write/%d 0..%d bytes %u times\n",
				param->vary, param->length, param->iterations);
		urb = simple_alloc_urb(udev, dev->out_pipe, param->length, 0);
		if (!urb) {
			retval = -ENOMEM;
			break;
		}
		/* FIRMWARE:  bulk sink (maybe accepts short writes) */
		retval = simple_io(dev, urb, param->iterations, param->vary,
					0, "test3");
		simple_free_urb(urb);
		break;
	case 4:
		if (dev->in_pipe == 0 || param->vary == 0)
			break;
		dev_info(&intf->dev,
				"TEST 4:  read/%d 0..%d bytes %u times\n",
				param->vary, param->length, param->iterations);
		urb = simple_alloc_urb(udev, dev->in_pipe, param->length, 0);
		if (!urb) {
			retval = -ENOMEM;
			break;
		}
		/* FIRMWARE:  bulk source (maybe generates short writes) */
		retval = simple_io(dev, urb, param->iterations, param->vary,
					0, "test4");
		simple_free_urb(urb);
		break;

	/* Queued bulk I/O tests */
	case 5:
		if (dev->out_pipe == 0 || param->sglen == 0)
			break;
		dev_info(&intf->dev,
			"TEST 5:  write %d sglists %d entries of %d bytes\n",
				param->iterations,
				param->sglen, param->length);
		sg = alloc_sglist(param->sglen, param->length,
				0, dev, dev->out_pipe);
		if (!sg) {
			retval = -ENOMEM;
			break;
		}
		/* FIRMWARE:  bulk sink (maybe accepts short writes) */
		retval = perform_sglist(dev, param->iterations, dev->out_pipe,
				&req, sg, param->sglen);
		free_sglist(sg, param->sglen);
		break;

	case 6:
		if (dev->in_pipe == 0 || param->sglen == 0)
			break;
		dev_info(&intf->dev,
			"TEST 6:  read %d sglists %d entries of %d bytes\n",
				param->iterations,
				param->sglen, param->length);
		sg = alloc_sglist(param->sglen, param->length,
				0, dev, dev->in_pipe);
		if (!sg) {
			retval = -ENOMEM;
			break;
		}
		/* FIRMWARE:  bulk source (maybe generates short writes) */
		retval = perform_sglist(dev, param->iterations, dev->in_pipe,
				&req, sg, param->sglen);
		free_sglist(sg, param->sglen);
		break;
	case 7:
		if (dev->out_pipe == 0 || param->sglen == 0 || param->vary == 0)
			break;
		dev_info(&intf->dev,
			"TEST 7:  write/%d %d sglists %d entries 0..%d bytes\n",
				param->vary, param->iterations,
				param->sglen, param->length);
		sg = alloc_sglist(param->sglen, param->length,
				param->vary, dev, dev->out_pipe);
		if (!sg) {
			retval = -ENOMEM;
			break;
		}
		/* FIRMWARE:  bulk sink (maybe accepts short writes) */
		retval = perform_sglist(dev, param->iterations, dev->out_pipe,
				&req, sg, param->sglen);
		free_sglist(sg, param->sglen);
		break;
	case 8:
		if (dev->in_pipe == 0 || param->sglen == 0 || param->vary == 0)
			break;
		dev_info(&intf->dev,
			"TEST 8:  read/%d %d sglists %d entries 0..%d bytes\n",
				param->vary, param->iterations,
				param->sglen, param->length);
		sg = alloc_sglist(param->sglen, param->length,
				param->vary, dev, dev->in_pipe);
		if (!sg) {
			retval = -ENOMEM;
			break;
		}
		/* FIRMWARE:  bulk source (maybe generates short writes) */
		retval = perform_sglist(dev, param->iterations, dev->in_pipe,
				&req, sg, param->sglen);
		free_sglist(sg, param->sglen);
		break;

	/* non-queued sanity tests for control (chapter 9 subset) */
	case 9:
		retval = 0;
		dev_info(&intf->dev,
			"TEST 9:  ch9 (subset) control tests, %d times\n",
				param->iterations);
		for (i = param->iterations; retval == 0 && i--; /* NOP */)
			retval = ch9_postconfig(dev);
		if (retval)
			dev_err(&intf->dev, "ch9 subset failed, "
					"iterations left %d\n", i);
		break;

	/* queued control messaging */
	case 10:
		retval = 0;
		dev_info(&intf->dev,
				"TEST 10:  queue %d control calls, %d times\n",
				param->sglen,
				param->iterations);
		retval = test_ctrl_queue(dev, param);
		break;

	/* simple non-queued unlinks (ring with one urb) */
	case 11:
		if (dev->in_pipe == 0 || !param->length)
			break;
		retval = 0;
		dev_info(&intf->dev, "TEST 11:  unlink %d reads of %d\n",
				param->iterations, param->length);
		for (i = param->iterations; retval == 0 && i--; /* NOP */)
			retval = unlink_simple(dev, dev->in_pipe,
						param->length);
		if (retval)
			dev_err(&intf->dev, "unlink reads failed %d, "
				"iterations left %d\n", retval, i);
		break;
	case 12:
		if (dev->out_pipe == 0 || !param->length)
			break;
		retval = 0;
		dev_info(&intf->dev, "TEST 12:  unlink %d writes of %d\n",
				param->iterations, param->length);
		for (i = param->iterations; retval == 0 && i--; /* NOP */)
			retval = unlink_simple(dev, dev->out_pipe,
						param->length);
		if (retval)
			dev_err(&intf->dev, "unlink writes failed %d, "
				"iterations left %d\n", retval, i);
		break;

	/* ep halt tests */
	case 13:
		if (dev->out_pipe == 0 && dev->in_pipe == 0)
			break;
		retval = 0;
		dev_info(&intf->dev, "TEST 13:  set/clear %d halts\n",
				param->iterations);
		for (i = param->iterations; retval == 0 && i--; /* NOP */)
			retval = halt_simple(dev);

		if (retval)
			ERROR(dev, "halts failed, iterations left %d\n", i);
		break;

	/* control write tests */
	case 14:
		if (!dev->info->ctrl_out)
			break;
		dev_info(&intf->dev, "TEST 14:  %d ep0out, %d..%d vary %d\n",
				param->iterations,
				realworld ? 1 : 0, param->length,
				param->vary);
		retval = ctrl_out(dev, param->iterations,
				param->length, param->vary, 0);
		break;

	/* iso write tests */
	case 15:
		if (dev->out_iso_pipe == 0 || param->sglen == 0)
			break;
		dev_info(&intf->dev,
			"TEST 15:  write %d iso, %d entries of %d bytes\n",
				param->iterations,
				param->sglen, param->length);
		/* FIRMWARE:  iso sink */
		retval = test_queue(dev, param,
				dev->out_iso_pipe, dev->iso_out, 0);
		break;

	/* iso read tests */
	case 16:
		if (dev->in_iso_pipe == 0 || param->sglen == 0)
			break;
		dev_info(&intf->dev,
			"TEST 16:  read %d iso, %d entries of %d bytes\n",
				param->iterations,
				param->sglen, param->length);
		/* FIRMWARE:  iso source */
		retval = test_queue(dev, param,
				dev->in_iso_pipe, dev->iso_in, 0);
		break;

	/* FIXME scatterlist cancel (needs helper thread) */

	/* Tests for bulk I/O using DMA mapping by core and odd address */
	case 17:
		if (dev->out_pipe == 0)
			break;
		dev_info(&intf->dev,
			"TEST 17:  write odd addr %d bytes %u times core map\n",
			param->length, param->iterations);

		retval = test_unaligned_bulk(
				dev, dev->out_pipe,
				param->length, param->iterations,
				0, "test17");
		break;

	case 18:
		if (dev->in_pipe == 0)
			break;
		dev_info(&intf->dev,
			"TEST 18:  read odd addr %d bytes %u times core map\n",
			param->length, param->iterations);

		retval = test_unaligned_bulk(
				dev, dev->in_pipe,
				param->length, param->iterations,
				0, "test18");
		break;

	/* Tests for bulk I/O using premapped coherent buffer and odd address */
	case 19:
		if (dev->out_pipe == 0)
			break;
		dev_info(&intf->dev,
			"TEST 19:  write odd addr %d bytes %u times premapped\n",
			param->length, param->iterations);

		retval = test_unaligned_bulk(
				dev, dev->out_pipe,
				param->length, param->iterations,
				URB_NO_TRANSFER_DMA_MAP, "test19");
		break;

	case 20:
		if (dev->in_pipe == 0)
			break;
		dev_info(&intf->dev,
			"TEST 20:  read odd addr %d bytes %u times premapped\n",
			param->length, param->iterations);

		retval = test_unaligned_bulk(
				dev, dev->in_pipe,
				param->length, param->iterations,
				URB_NO_TRANSFER_DMA_MAP, "test20");
		break;

	/* control write tests with unaligned buffer */
	case 21:
		if (!dev->info->ctrl_out)
			break;
		dev_info(&intf->dev,
				"TEST 21:  %d ep0out odd addr, %d..%d vary %d\n",
				param->iterations,
				realworld ? 1 : 0, param->length,
				param->vary);
		retval = ctrl_out(dev, param->iterations,
				param->length, param->vary, 1);
		break;

	/* unaligned iso tests */
	case 22:
		if (dev->out_iso_pipe == 0 || param->sglen == 0)
			break;
		dev_info(&intf->dev,
			"TEST 22:  write %d iso odd, %d entries of %d bytes\n",
				param->iterations,
				param->sglen, param->length);
		retval = test_queue(dev, param,
				dev->out_iso_pipe, dev->iso_out, 1);
		break;

	case 23:
		if (dev->in_iso_pipe == 0 || param->sglen == 0)
			break;
		dev_info(&intf->dev,
			"TEST 23:  read %d iso odd, %d entries of %d bytes\n",
				param->iterations,
				param->sglen, param->length);
		retval = test_queue(dev, param,
				dev->in_iso_pipe, dev->iso_in, 1);
		break;

	/* unlink URBs from a bulk-OUT queue */
	case 24:
		if (dev->out_pipe == 0 || !param->length || param->sglen < 4)
			break;
		retval = 0;
		dev_info(&intf->dev, "TEST 24:  unlink from %d queues of "
				"%d %d-byte writes\n",
				param->iterations, param->sglen, param->length);
		for (i = param->iterations; retval == 0 && i > 0; --i) {
			retval = unlink_queued(dev, dev->out_pipe,
						param->sglen, param->length);
			if (retval) {
				dev_err(&intf->dev,
					"unlink queued writes failed %d, "
					"iterations left %d\n", retval, i);
				break;
			}
		}
		break;

	/* Simple non-queued interrupt I/O tests */
	case 25:
		if (dev->out_int_pipe == 0)
			break;
		dev_info(&intf->dev,
				"TEST 25: write %d bytes %u times\n",
				param->length, param->iterations);
		urb = simple_alloc_urb(udev, dev->out_int_pipe, param->length,
				dev->int_out->bInterval);
		if (!urb) {
			retval = -ENOMEM;
			break;
		}
		/* FIRMWARE: interrupt sink (maybe accepts short writes) */
		retval = simple_io(dev, urb, param->iterations, 0, 0, "test25");
		simple_free_urb(urb);
		break;
	case 26:
		if (dev->in_int_pipe == 0)
			break;
		dev_info(&intf->dev,
				"TEST 26: read %d bytes %u times\n",
				param->length, param->iterations);
		urb = simple_alloc_urb(udev, dev->in_int_pipe, param->length,
				dev->int_in->bInterval);
		if (!urb) {
			retval = -ENOMEM;
			break;
		}
		/* FIRMWARE: interrupt source (maybe generates short writes) */
		retval = simple_io(dev, urb, param->iterations, 0, 0, "test26");
		simple_free_urb(urb);
		break;
	case 27:
		/* We do performance test, so ignore data compare */
		if (dev->out_pipe == 0 || param->sglen == 0 || pattern != 0)
			break;
		dev_info(&intf->dev,
			"TEST 27: bulk write %dMbytes\n", (param->iterations *
			param->sglen * param->length) / (1024 * 1024));
		retval = test_queue(dev, param,
				dev->out_pipe, NULL, 0);
		break;
	case 28:
		if (dev->in_pipe == 0 || param->sglen == 0 || pattern != 0)
			break;
		dev_info(&intf->dev,
			"TEST 28: bulk read %dMbytes\n", (param->iterations *
			param->sglen * param->length) / (1024 * 1024));
		retval = test_queue(dev, param,
				dev->in_pipe, NULL, 0);
		break;
	/* Test data Toggle/seq_nr clear between bulk out transfers */
	case 29:
		if (dev->out_pipe == 0)
			break;
		retval = 0;
		dev_info(&intf->dev, "TEST 29: Clear toggle between bulk writes %d times\n",
				param->iterations);
		for (i = param->iterations; retval == 0 && i > 0; --i)
			retval = toggle_sync_simple(dev);

		if (retval)
			ERROR(dev, "toggle sync failed, iterations left %d\n",
			      i);
		break;
	}
	return retval;
}

static int override_alt = -1;

static void complicated_callback(struct urb *urb);

struct usbtest_param_32 {
	/* inputs */
	__u32		test_num;	/* 0..(TEST_CASES-1) */
	__u32		iterations;
	__u32		length;
	__u32		vary;
	__u32		sglen;

	/* outputs */
	__s32		duration_sec;
	__s32		duration_usec;
}

struct usbtest_param_64 {
	/* inputs */
	__u32		test_num;	/* 0..(TEST_CASES-1) */
	__u32		iterations;
	__u32		length;
	__u32		vary;
	__u32		sglen;

	/* outputs */
	__s64		duration_sec;
	__s64		duration_usec;
}

#define USBTEST_REQUEST_32    _IOWR('U', 100, struct usbtest_param_32)

#define USBTEST_REQUEST_64    _IOWR('U', 100, struct usbtest_param_64)

#define	GENERIC		/* let probe() bind using module params */

struct usbtest_info {
	const char		*name;
	u8			ep_in;		/* bulk/intr source */
	u8			ep_out;		/* bulk/intr sink */
	unsigned		autoconf:1;
	unsigned		ctrl_out:1;
	unsigned		iso:1;		/* try iso in/out */
	unsigned		intr:1;		/* try interrupt in/out */
	int			alt;
}

struct usbtest_dev {
	struct usb_interface	*intf;
	struct usbtest_info	*info;
	int			in_pipe;
	int			out_pipe;
	int			in_iso_pipe;
	int			out_iso_pipe;
	int			in_int_pipe;
	int			out_int_pipe;
	struct usb_endpoint_descriptor	*iso_in, *iso_out;
	struct usb_endpoint_descriptor	*int_in, *int_out;
	struct mutex		lock;

#define TBUF_SIZE	256
	u8			*buf;
}

#define ERROR(tdev, fmt, args...) \
	dev_err(&(tdev)->intf->dev , fmt , ## args)

#define WARNING(tdev, fmt, args...) \
	dev_warn(&(tdev)->intf->dev , fmt , ## args)

#define MAX_SGLEN	128

static unsigned pattern;

static unsigned mod_pattern;

struct sg_timeout {
	struct timer_list timer;
	struct usb_sg_request *req;
}

static unsigned realworld = 1;

struct ctrl_ctx {
	spinlock_t		lock;
	struct usbtest_dev	*dev;
	struct completion	complete;
	unsigned		count;
	unsigned		pending;
	int			status;
	struct urb		**urb;
	struct usbtest_param_32	*param;
	int			last;
}

#define NUM_SUBCASES	16		/* how many test subcases here? */

struct subcase {
	struct usb_ctrlrequest	setup;
	int			number;
	int			expected;
}

struct queued_ctx {
	struct completion	complete;
	atomic_t		pending;
	unsigned		num;
	int			status;
	struct urb		**urbs;
}

struct transfer_context {
	unsigned		count;
	unsigned		pending;
	spinlock_t		lock;
	struct completion	done;
	int			submit_error;
	unsigned long		errors;
	unsigned long		packet_count;
	struct usbtest_dev	*dev;
	bool			is_iso;
}

static unsigned force_interrupt;

static unsigned short vendor;

static unsigned short product;


static void free_sglist(struct scatterlist *sg, int nents)
{
	unsigned		i;

	if (!sg)
		return;
	for (i = 0; i < nents; i++) {
		if (!sg_page(&sg[i]))
			continue;
		kfree(sg_virt(&sg[i]));
	}
	kfree(sg);
}


static int unlink_queued(struct usbtest_dev *dev, int pipe, unsigned num,
		unsigned size)
{
	struct queued_ctx	ctx;
	struct usb_device	*udev = testdev_to_usbdev(dev);
	void			*buf;
	dma_addr_t		buf_dma;
	int			i;
	int			retval = -ENOMEM;

	init_completion(&ctx.complete);
	atomic_set(&ctx.pending, 1);	/* One more than the actual value */
	ctx.num = num;
	ctx.status = 0;

	buf = usb_alloc_coherent(udev, size, GFP_KERNEL, &buf_dma);
	if (!buf)
		return retval;
	memset(buf, 0, size);

	/* Allocate and init the urbs we'll queue */
	ctx.urbs = kcalloc(num, sizeof(struct urb *), GFP_KERNEL);
	if (!ctx.urbs)
		goto free_buf;
	for (i = 0; i < num; i++) {
		ctx.urbs[i] = usb_alloc_urb(0, GFP_KERNEL);
		if (!ctx.urbs[i])
			goto free_urbs;
		usb_fill_bulk_urb(ctx.urbs[i], udev, pipe, buf, size,
				unlink_queued_callback, &ctx);
		ctx.urbs[i]->transfer_dma = buf_dma;
		ctx.urbs[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP;

		if (usb_pipeout(ctx.urbs[i]->pipe)) {
			simple_fill_buf(ctx.urbs[i]);
			ctx.urbs[i]->transfer_flags |= URB_ZERO_PACKET;
		}
	}

	/* Submit all the URBs and then unlink URBs num - 4 and num - 2. */
	for (i = 0; i < num; i++) {
		atomic_inc(&ctx.pending);
		retval = usb_submit_urb(ctx.urbs[i], GFP_KERNEL);
		if (retval != 0) {
			dev_err(&dev->intf->dev, "submit urbs[%d] fail %d\n",
					i, retval);
			atomic_dec(&ctx.pending);
			ctx.status = retval;
			break;
		}
	}
	if (i == num) {
		usb_unlink_urb(ctx.urbs[num - 4]);
		usb_unlink_urb(ctx.urbs[num - 2]);
	} else {
		while (--i >= 0)
			usb_unlink_urb(ctx.urbs[i]);
	}

	if (atomic_dec_and_test(&ctx.pending))		/* The extra count */
		complete(&ctx.complete);
	wait_for_completion(&ctx.complete);
	retval = ctx.status;

 free_urbs:
	for (i = 0; i < num; i++)
		usb_free_urb(ctx.urbs[i]);
	kfree(ctx.urbs);
 free_buf:
	usb_free_coherent(udev, size, buf, buf_dma);
	return retval;
}


static int usbtest_suspend(struct usb_interface *intf, pm_message_t message)
{
	return 0;
}


static int ctrl_out(struct usbtest_dev *dev,
		unsigned count, unsigned length, unsigned vary, unsigned offset)
{
	unsigned		i, j, len;
	int			retval;
	u8			*buf;
	char			*what = "?";
	struct usb_device	*udev;

	if (length < 1 || length > 0xffff || vary >= length)
		return -EINVAL;

	buf = kmalloc(length + offset, GFP_KERNEL);
	if (!buf)
		return -ENOMEM;

	buf += offset;
	udev = testdev_to_usbdev(dev);
	len = length;
	retval = 0;

	/* NOTE:  hardware might well act differently if we pushed it
	 * with lots back-to-back queued requests.
	 */
	for (i = 0; i < count; i++) {
		/* write patterned data */
		for (j = 0; j < len; j++)
			buf[j] = (u8)(i + j);
		retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
				0x5b, USB_DIR_OUT|USB_TYPE_VENDOR,
				0, 0, buf, len, USB_CTRL_SET_TIMEOUT);
		if (retval != len) {
			what = "write";
			if (retval >= 0) {
				ERROR(dev, "ctrl_out, wlen %d (expected %d)\n",
						retval, len);
				retval = -EBADMSG;
			}
			break;
		}

		/* read it back -- assuming nothing intervened!!  */
		retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
				0x5c, USB_DIR_IN|USB_TYPE_VENDOR,
				0, 0, buf, len, USB_CTRL_GET_TIMEOUT);
		if (retval != len) {
			what = "read";
			if (retval >= 0) {
				ERROR(dev, "ctrl_out, rlen %d (expected %d)\n",
						retval, len);
				retval = -EBADMSG;
			}
			break;
		}

		/* fail if we can't verify */
		for (j = 0; j < len; j++) {
			if (buf[j] != (u8)(i + j)) {
				ERROR(dev, "ctrl_out, byte %d is %d not %d\n",
					j, buf[j], (u8)(i + j));
				retval = -EBADMSG;
				break;
			}
		}
		if (retval < 0) {
			what = "verify";
			break;
		}

		len += vary;

		/* [real world] the "zero bytes IN" case isn't really used.
		 * hardware can easily trip up in this weird case, since its
		 * status stage is IN, not OUT like other ep0in transfers.
		 */
		if (len > length)
			len = realworld ? 1 : 0;
	}

	if (retval < 0)
		ERROR(dev, "ctrl_out %s failed, code %d, count %d\n",
			what, retval, i);

	kfree(buf - offset);
	return retval;
}


static int is_good_ext(struct usbtest_dev *tdev, u8 *buf)
{
	struct usb_ext_cap_descriptor *ext;
	u32 attr;

	ext = (struct usb_ext_cap_descriptor *) buf;

	if (ext->bLength != USB_DT_USB_EXT_CAP_SIZE) {
		ERROR(tdev, "bogus usb 2.0 extension descriptor length\n");
		return 0;
	}

	attr = le32_to_cpu(ext->bmAttributes);
	/* bits[1:15] is used and others are reserved */
	if (attr & ~0xfffe) {	/* reserved == 0 */
		ERROR(tdev, "reserved bits set\n");
		return 0;
	}

	return 1;
}


static struct urb *simple_alloc_urb(
	struct usb_device	*udev,
	int			pipe,
	unsigned long		bytes,
	u8			bInterval)
{
	return usbtest_alloc_urb(udev, pipe, bytes, URB_NO_TRANSFER_DMA_MAP, 0,
			bInterval, simple_callback);
}


static int verify_halted(struct usbtest_dev *tdev, int ep, struct urb *urb)
{
	int	retval;
	u16	status;

	/* should look and act halted */
	retval = usb_get_std_status(urb->dev, USB_RECIP_ENDPOINT, ep, &status);
	if (retval < 0) {
		ERROR(tdev, "ep %02x couldn't get halt status, %d\n",
				ep, retval);
		return retval;
	}
	if (status != 1) {
		ERROR(tdev, "ep %02x bogus status: %04x != 1\n", ep, status);
		return -EINVAL;
	}
	retval = simple_io(tdev, urb, 1, 0, -EPIPE, __func__);
	if (retval != -EPIPE)
		return -EINVAL;
	retval = simple_io(tdev, urb, 1, 0, -EPIPE, "verify_still_halted");
	if (retval != -EPIPE)
		return -EINVAL;
	return 0;
}


static int unlink_simple(struct usbtest_dev *dev, int pipe, int len)
{
	int			retval = 0;

	/* test sync and async paths */
	retval = unlink1(dev, pipe, len, 1);
	if (!retval)
		retval = unlink1(dev, pipe, len, 0);
	return retval;
}


static struct urb *complicated_alloc_urb(
	struct usb_device	*udev,
	int			pipe,
	unsigned long		bytes,
	u8			bInterval)
{
	return usbtest_alloc_urb(udev, pipe, bytes, URB_NO_TRANSFER_DMA_MAP, 0,
			bInterval, complicated_callback);
}


get_endpoints(struct usbtest_dev *dev, struct usb_interface *intf)
{
	int				tmp;
	struct usb_host_interface	*alt;
	struct usb_host_endpoint	*in, *out;
	struct usb_host_endpoint	*iso_in, *iso_out;
	struct usb_host_endpoint	*int_in, *int_out;
	struct usb_device		*udev;

	for (tmp = 0; tmp < intf->num_altsetting; tmp++) {
		unsigned	ep;

		in = out = NULL;
		iso_in = iso_out = NULL;
		int_in = int_out = NULL;
		alt = intf->altsetting + tmp;

		if (override_alt >= 0 &&
				override_alt != alt->desc.bAlternateSetting)
			continue;

		/* take the first altsetting with in-bulk + out-bulk;
		 * ignore other endpoints and altsettings.
		 */
		for (ep = 0; ep < alt->desc.bNumEndpoints; ep++) {
			struct usb_host_endpoint	*e;
			int edi;

			e = alt->endpoint + ep;
			edi = usb_endpoint_dir_in(&e->desc);

			switch (usb_endpoint_type(&e->desc)) {
			case USB_ENDPOINT_XFER_BULK:
				endpoint_update(edi, &in, &out, e);
				continue;
			case USB_ENDPOINT_XFER_INT:
				if (dev->info->intr)
					endpoint_update(edi, &int_in, &int_out, e);
				continue;
			case USB_ENDPOINT_XFER_ISOC:
				if (dev->info->iso)
					endpoint_update(edi, &iso_in, &iso_out, e);
				/* FALLTHROUGH */
			default:
				continue;
			}
		}
		if ((in && out)  ||  iso_in || iso_out || int_in || int_out)
			goto found;
	}
	return -EINVAL;

found:
	udev = testdev_to_usbdev(dev);
	dev->info->alt = alt->desc.bAlternateSetting;
	if (alt->desc.bAlternateSetting != 0) {
		tmp = usb_set_interface(udev,
				alt->desc.bInterfaceNumber,
				alt->desc.bAlternateSetting);
		if (tmp < 0)
			return tmp;
	}

	if (in)
		dev->in_pipe = usb_rcvbulkpipe(udev,
			in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
	if (out)
		dev->out_pipe = usb_sndbulkpipe(udev,
			out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);

	if (iso_in) {
		dev->iso_in = &iso_in->desc;
		dev->in_iso_pipe = usb_rcvisocpipe(udev,
				iso_in->desc.bEndpointAddress
					& USB_ENDPOINT_NUMBER_MASK);
	}

	if (iso_out) {
		dev->iso_out = &iso_out->desc;
		dev->out_iso_pipe = usb_sndisocpipe(udev,
				iso_out->desc.bEndpointAddress
					& USB_ENDPOINT_NUMBER_MASK);
	}

	if (int_in) {
		dev->int_in = &int_in->desc;
		dev->in_int_pipe = usb_rcvintpipe(udev,
				int_in->desc.bEndpointAddress
					& USB_ENDPOINT_NUMBER_MASK);
	}

	if (int_out) {
		dev->int_out = &int_out->desc;
		dev->out_int_pipe = usb_sndintpipe(udev,
				int_out->desc.bEndpointAddress
					& USB_ENDPOINT_NUMBER_MASK);
	}
	return 0;
}


static int is_good_config(struct usbtest_dev *tdev, int len)
{
	struct usb_config_descriptor	*config;

	if (len < sizeof(*config))
		return 0;
	config = (struct usb_config_descriptor *) tdev->buf;

	switch (config->bDescriptorType) {
	case USB_DT_CONFIG:
	case USB_DT_OTHER_SPEED_CONFIG:
		if (config->bLength != 9) {
			ERROR(tdev, "bogus config descriptor length\n");
			return 0;
		}
		/* this bit 'must be 1' but often isn't */
		if (!realworld && !(config->bmAttributes & 0x80)) {
			ERROR(tdev, "high bit of config attributes not set\n");
			return 0;
		}
		if (config->bmAttributes & 0x1f) {	/* reserved == 0 */
			ERROR(tdev, "reserved config bits set\n");
			return 0;
		}
		break;
	default:
		return 0;
	}

	if (le16_to_cpu(config->wTotalLength) == len)	/* read it all */
		return 1;
	if (le16_to_cpu(config->wTotalLength) >= TBUF_SIZE)	/* max partial read */
		return 1;
	ERROR(tdev, "bogus config descriptor read size\n");
	return 0;
}


test_queue(struct usbtest_dev *dev, struct usbtest_param_32 *param,
		int pipe, struct usb_endpoint_descriptor *desc, unsigned offset)
{
	struct transfer_context	context;
	struct usb_device	*udev;
	unsigned		i;
	unsigned long		packets = 0;
	int			status = 0;
	struct urb		*urbs[MAX_SGLEN];

	if (!param->sglen || param->iterations > UINT_MAX / param->sglen)
		return -EINVAL;

	if (param->sglen > MAX_SGLEN)
		return -EINVAL;

	memset(&context, 0, sizeof(context));
	context.count = param->iterations * param->sglen;
	context.dev = dev;
	context.is_iso = !!desc;
	init_completion(&context.done);
	spin_lock_init(&context.lock);

	udev = testdev_to_usbdev(dev);

	for (i = 0; i < param->sglen; i++) {
		if (context.is_iso)
			urbs[i] = iso_alloc_urb(udev, pipe, desc,
					param->length, offset);
		else
			urbs[i] = complicated_alloc_urb(udev, pipe,
					param->length, 0);

		if (!urbs[i]) {
			status = -ENOMEM;
			goto fail;
		}
		packets += urbs[i]->number_of_packets;
		urbs[i]->context = &context;
	}
	packets *= param->iterations;

	if (context.is_iso) {
		int transaction_num;

		if (udev->speed >= USB_SPEED_SUPER)
			transaction_num = ss_isoc_get_packet_num(udev, pipe);
		else
			transaction_num = usb_endpoint_maxp_mult(desc);

		dev_info(&dev->intf->dev,
			"iso period %d %sframes, wMaxPacket %d, transactions: %d\n",
			1 << (desc->bInterval - 1),
			(udev->speed >= USB_SPEED_HIGH) ? "micro" : "",
			usb_endpoint_maxp(desc),
			transaction_num);

		dev_info(&dev->intf->dev,
			"total %lu msec (%lu packets)\n",
			(packets * (1 << (desc->bInterval - 1)))
				/ ((udev->speed >= USB_SPEED_HIGH) ? 8 : 1),
			packets);
	}

	spin_lock_irq(&context.lock);
	for (i = 0; i < param->sglen; i++) {
		++context.pending;
		status = usb_submit_urb(urbs[i], GFP_ATOMIC);
		if (status < 0) {
			ERROR(dev, "submit iso[%d], error %d\n", i, status);
			if (i == 0) {
				spin_unlock_irq(&context.lock);
				goto fail;
			}

			simple_free_urb(urbs[i]);
			urbs[i] = NULL;
			context.pending--;
			context.submit_error = 1;
			break;
		}
	}
	spin_unlock_irq(&context.lock);

	wait_for_completion(&context.done);

	for (i = 0; i < param->sglen; i++) {
		if (urbs[i])
			simple_free_urb(urbs[i]);
	}
	/*
	 * Isochronous transfers are expected to fail sometimes.  As an
	 * arbitrary limit, we will report an error if any submissions
	 * fail or if the transfer failure rate is > 10%.
	 */
	if (status != 0)
		;
	else if (context.submit_error)
		status = -EACCES;
	else if (context.errors >
			(context.is_iso ? context.packet_count / 10 : 0))
		status = -EIO;
	return status;

fail:
	for (i = 0; i < param->sglen; i++) {
		if (urbs[i])
			simple_free_urb(urbs[i]);
	}
	return status;
}

static int override_alt = -1;

static void complicated_callback(struct urb *urb);

struct usbtest_param_32 {
	/* inputs */
	__u32		test_num;	/* 0..(TEST_CASES-1) */
	__u32		iterations;
	__u32		length;
	__u32		vary;
	__u32		sglen;

	/* outputs */
	__s32		duration_sec;
	__s32		duration_usec;
}

struct usbtest_param_64 {
	/* inputs */
	__u32		test_num;	/* 0..(TEST_CASES-1) */
	__u32		iterations;
	__u32		length;
	__u32		vary;
	__u32		sglen;

	/* outputs */
	__s64		duration_sec;
	__s64		duration_usec;
}

#define USBTEST_REQUEST_32    _IOWR('U', 100, struct usbtest_param_32)

#define USBTEST_REQUEST_64    _IOWR('U', 100, struct usbtest_param_64)

struct usbtest_info {
	const char		*name;
	u8			ep_in;		/* bulk/intr source */
	u8			ep_out;		/* bulk/intr sink */
	unsigned		autoconf:1;
	unsigned		ctrl_out:1;
	unsigned		iso:1;		/* try iso in/out */
	unsigned		intr:1;		/* try interrupt in/out */
	int			alt;
}

struct usbtest_dev {
	struct usb_interface	*intf;
	struct usbtest_info	*info;
	int			in_pipe;
	int			out_pipe;
	int			in_iso_pipe;
	int			out_iso_pipe;
	int			in_int_pipe;
	int			out_int_pipe;
	struct usb_endpoint_descriptor	*iso_in, *iso_out;
	struct usb_endpoint_descriptor	*int_in, *int_out;
	struct mutex		lock;

#define TBUF_SIZE	256
	u8			*buf;
}

#define	INTERRUPT_RATE		1	/* msec/transfer */

#define ERROR(tdev, fmt, args...) \
	dev_err(&(tdev)->intf->dev , fmt , ## args)

#define WARNING(tdev, fmt, args...) \
	dev_warn(&(tdev)->intf->dev , fmt , ## args)

#define MAX_SGLEN	128

static unsigned pattern;

static unsigned mod_pattern;

struct sg_timeout {
	struct timer_list timer;
	struct usb_sg_request *req;
}

static unsigned realworld = 1;

struct ctrl_ctx {
	spinlock_t		lock;
	struct usbtest_dev	*dev;
	struct completion	complete;
	unsigned		count;
	unsigned		pending;
	int			status;
	struct urb		**urb;
	struct usbtest_param_32	*param;
	int			last;
}

struct subcase {
	struct usb_ctrlrequest	setup;
	int			number;
	int			expected;
}

struct queued_ctx {
	struct completion	complete;
	atomic_t		pending;
	unsigned		num;
	int			status;
	struct urb		**urbs;
}

struct transfer_context {
	unsigned		count;
	unsigned		pending;
	spinlock_t		lock;
	struct completion	done;
	int			submit_error;
	unsigned long		errors;
	unsigned long		packet_count;
	struct usbtest_dev	*dev;
	bool			is_iso;
}

static unsigned force_interrupt;

static unsigned short vendor;

static unsigned short product;

static struct usbtest_info ez1_info = {
	.name		= "EZ-USB device",
	.ep_in		= 2,
	.ep_out		= 2,
	.alt		= 1,
};


static void unlink1_callback(struct urb *urb)
{
	int	status = urb->status;

	/* we "know" -EPIPE (stall) never happens */
	if (!status)
		status = usb_submit_urb(urb, GFP_ATOMIC);
	if (status) {
		urb->status = status;
		complete(urb->context);
	}
}


static int test_toggle_sync(struct usbtest_dev *tdev, int ep, struct urb *urb)
{
	int	retval;

	/* clear initial data toggle to DATA0 */
	retval = usb_clear_halt(urb->dev, urb->pipe);
	if (retval < 0) {
		ERROR(tdev, "ep %02x couldn't clear halt, %d\n", ep, retval);
		return retval;
	}

	/* transfer 3 data packets, should be DATA0, DATA1, DATA0 */
	retval = simple_io(tdev, urb, 1, 0, 0, __func__);
	if (retval != 0)
		return -EINVAL;

	/* clear halt resets device side data toggle, host should react to it */
	retval = usb_clear_halt(urb->dev, urb->pipe);
	if (retval < 0) {
		ERROR(tdev, "ep %02x couldn't clear halt, %d\n", ep, retval);
		return retval;
	}

	/* host should use DATA0 again after clear halt */
	retval = simple_io(tdev, urb, 1, 0, 0, __func__);

	return retval;
}

static int override_alt = -1;

static void complicated_callback(struct urb *urb);

struct usbtest_param_32 {
	/* inputs */
	__u32		test_num;	/* 0..(TEST_CASES-1) */
	__u32		iterations;
	__u32		length;
	__u32		vary;
	__u32		sglen;

	/* outputs */
	__s32		duration_sec;
	__s32		duration_usec;
}

struct usbtest_param_64 {
	/* inputs */
	__u32		test_num;	/* 0..(TEST_CASES-1) */
	__u32		iterations;
	__u32		length;
	__u32		vary;
	__u32		sglen;

	/* outputs */
	__s64		duration_sec;
	__s64		duration_usec;
}

#define USBTEST_REQUEST_32    _IOWR('U', 100, struct usbtest_param_32)

#define USBTEST_REQUEST_64    _IOWR('U', 100, struct usbtest_param_64)

struct usbtest_info {
	const char		*name;
	u8			ep_in;		/* bulk/intr source */
	u8			ep_out;		/* bulk/intr sink */
	unsigned		autoconf:1;
	unsigned		ctrl_out:1;
	unsigned		iso:1;		/* try iso in/out */
	unsigned		intr:1;		/* try interrupt in/out */
	int			alt;
}

struct usbtest_dev {
	struct usb_interface	*intf;
	struct usbtest_info	*info;
	int			in_pipe;
	int			out_pipe;
	int			in_iso_pipe;
	int			out_iso_pipe;
	int			in_int_pipe;
	int			out_int_pipe;
	struct usb_endpoint_descriptor	*iso_in, *iso_out;
	struct usb_endpoint_descriptor	*int_in, *int_out;
	struct mutex		lock;

#define TBUF_SIZE	256
	u8			*buf;
}

#define	INTERRUPT_RATE		1	/* msec/transfer */

#define ERROR(tdev, fmt, args...) \
	dev_err(&(tdev)->intf->dev , fmt , ## args)

#define WARNING(tdev, fmt, args...) \
	dev_warn(&(tdev)->intf->dev , fmt , ## args)

static unsigned pattern;

static unsigned mod_pattern;

struct sg_timeout {
	struct timer_list timer;
	struct usb_sg_request *req;
}

static unsigned realworld = 1;

struct ctrl_ctx {
	spinlock_t		lock;
	struct usbtest_dev	*dev;
	struct completion	complete;
	unsigned		count;
	unsigned		pending;
	int			status;
	struct urb		**urb;
	struct usbtest_param_32	*param;
	int			last;
}

struct subcase {
	struct usb_ctrlrequest	setup;
	int			number;
	int			expected;
}

struct queued_ctx {
	struct completion	complete;
	atomic_t		pending;
	unsigned		num;
	int			status;
	struct urb		**urbs;
}

struct transfer_context {
	unsigned		count;
	unsigned		pending;
	spinlock_t		lock;
	struct completion	done;
	int			submit_error;
	unsigned long		errors;
	unsigned long		packet_count;
	struct usbtest_dev	*dev;
	bool			is_iso;
}

static unsigned force_interrupt;

static unsigned short vendor;

static unsigned short product;

static struct usbtest_info ez1_info = {
	.name		= "EZ-USB device",
	.ep_in		= 2,
	.ep_out		= 2,
	.alt		= 1,
};


PHP_FUNCTION(quoted_printable_decode)
{
	char *arg1, *str_in, *str_out;
	int arg1_len, i = 0, j = 0, k;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg1, &arg1_len) == FAILURE) {
		return;
	}

	if (arg1_len == 0) {
		/* shortcut */
		RETURN_EMPTY_STRING();
	}

	str_in = arg1;
	str_out = emalloc(arg1_len + 1);
	while (str_in[i]) {
		switch (str_in[i]) {
		case '=':
			if (str_in[i + 1] && str_in[i + 2] &&
				isxdigit((int) str_in[i + 1]) &&
				isxdigit((int) str_in[i + 2]))
			{
				str_out[j++] = (php_hex2int((int) str_in[i + 1]) << 4)
						+ php_hex2int((int) str_in[i + 2]);
				i += 3;
			} else  /* check for soft line break according to RFC 2045*/ {
				k = 1;
				while (str_in[i + k] && ((str_in[i + k] == 32) || (str_in[i + k] == 9))) {
					/* Possibly, skip spaces/tabs at the end of line */
					k++;
				}
				if (!str_in[i + k]) {
					/* End of line reached */
					i += k;
				}
				else if ((str_in[i + k] == 13) && (str_in[i + k + 1] == 10)) {
					/* CRLF */
					i += k + 2;
				}
				else if ((str_in[i + k] == 13) || (str_in[i + k] == 10)) {
					/* CR or LF */
					i += k + 1;
				}
				else {
					str_out[j++] = str_in[i++];
				}
			}
			break;
		default:
			str_out[j++] = str_in[i++];
		}
	}
	str_out[j] = '\0';

	RETVAL_STRINGL(str_out, j, 0);
}

Bug 62462: Prevent multibyte characters from being split between the lines

Merged from https://github.com/php/php-src/pull/120


static char php_hex2int(int c) /* {{{ */
{
	if (isdigit(c)) {
		return c - '0';
	}
	else if (c >= 'A' && c <= 'F') {
		return c - 'A' + 10;
	}
	else if (c >= 'a' && c <= 'f') {
		return c - 'a' + 10;
	}
	else {
		return -1;
	}
}


PHP_FUNCTION(quoted_printable_encode)
{
	char *str, *new_str;
	int str_len;
	size_t new_str_len;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) != SUCCESS) {
		return;
	}

	if (!str_len) {
		RETURN_EMPTY_STRING();
	}

	new_str = (char *)php_quot_print_encode((unsigned char *)str, (size_t)str_len, &new_str_len);
	RETURN_STRINGL(new_str, new_str_len, 0);
}


HRESULT Http::HrGetDepth(ULONG *ulDepth)
{
	std::string strDepth;
	/*
	 * Valid input: [0, 1, infinity]
	 */
	auto hr = HrGetHeaderValue("Depth", &strDepth);
	if (hr != hrSuccess)
		*ulDepth = 0; /* Default is no subfolders. Default should become a parameter. It is action dependent. */
	else if (strDepth == "infinity")
		*ulDepth = 2;
	else {
		*ulDepth = atoi(strDepth.c_str());
		if (*ulDepth > 1)
			*ulDepth = 1;
	}
	return hr;
}

Set limit on header size to prevent bad alloc

This sets a hard limit of 64 KiB to the header to prevent a memory
allocation exception from being thrown during the parsing of the request
headers.

/* No explicit global definitions found. */


HRESULT Http::HrGetHeaderValue(const std::string &strHeader, std::string *strValue)
{
	auto iHeader = mapHeaders.find(strHeader);
	if (iHeader == mapHeaders.cend())
		return MAPI_E_NOT_FOUND;
	*strValue = iHeader->second;
	return hrSuccess;
}


HRESULT Http::HrGetRequestUrl(std::string *strURL)
{
	strURL->assign(m_strURL);
	return hrSuccess;
}


bool Http::CheckIfMatch(LPMAPIPROP lpProp)
{
	bool ret = false, invert = false;
	std::string strIf, strValue;
	memory_ptr<SPropValue> ptrLastModTime;

	if (lpProp != nullptr &&
	    HrGetOneProp(lpProp, PR_LAST_MODIFICATION_TIME, &~ptrLastModTime) == hrSuccess)
		strValue = stringify_int64(FileTimeToUnixTime(ptrLastModTime->Value.ft), false);

	if (HrGetHeaderValue("If-Match", &strIf) == hrSuccess) {
		if (strIf == "*" && ptrLastModTime == nullptr)
			// we have an object without a last mod time, not allowed
			return false;
	} else if (HrGetHeaderValue("If-None-Match", &strIf) == hrSuccess) {
		if (strIf == "*" && ptrLastModTime != nullptr)
			// we have an object which has a last mod time, not allowed
			return false;
		invert = true;
	} else {
		return true;
	}

	// check all etags for a match
	for (auto &i : tokenize(strIf, ',', true)) {
		if (i.at(0) == '"' || i.at(0) == '\'')
			i.assign(i.begin() + 1, i.end() - 1);
		if (i == strValue) {
			ret = true;
			break;
		}
	}
	if (invert)
		ret = !ret;
	return ret;
}


HRESULT Http::HrToHTTPCode(HRESULT hr)
{
	if (hr == hrSuccess)
		return HrResponseHeader(200, "Ok");
	else if (hr == MAPI_E_NO_ACCESS)
		return HrResponseHeader(403, "Forbidden");
	else if (hr == MAPI_E_NOT_FOUND)
		return HrResponseHeader(404, "Not Found");
	// @todo other codes?
	return HrResponseHeader(500, "Unhanded error " + stringify_hex(hr));
}


HRESULT Http::HrGetUserAgent(std::string *strUserAgent)
{
	if (m_strUserAgent.empty())
		return MAPI_E_NOT_FOUND;
	strUserAgent -> assign(m_strUserAgent);
	return hrSuccess;
}


HRESULT Http::HrReadHeaders()
{
	HRESULT hr;
	std::string strBuffer;
	ULONG n = 0;
	std::map<std::string, std::string>::iterator iHeader = mapHeaders.end();
	static constexpr std::size_t MAX_HEADER_LENGTH = 65536;
	std::size_t numOfBytesRead = 0;

	ec_log_debug("Receiving headers:");
	do
	{
		hr = m_lpChannel->HrReadLine(strBuffer);
		if (hr != hrSuccess)
			return hr;
		if (strBuffer.empty())
			break;

		numOfBytesRead += strBuffer.size();
		if(numOfBytesRead > MAX_HEADER_LENGTH) {
			return MAPI_E_TOO_BIG;
		}

		if (n == 0) {
			m_strAction = strBuffer;
		} else {
			auto pos = strBuffer.find(':');
			size_t start = 0;

			if (strBuffer[0] == ' ' || strBuffer[0] == '\t') {
				if (iHeader == mapHeaders.end())
					continue;
				// continue header
				while (strBuffer[start] == ' ' || strBuffer[start] == '\t')
					++start;
				iHeader->second += strBuffer.substr(start);
			} else {
				// new header
				auto r = mapHeaders.emplace(strBuffer.substr(0, pos), strBuffer.substr(pos + 2));
				iHeader = r.first;
			}
		}

		if (strBuffer.find("Authorization") != std::string::npos)
			ec_log_debug("< Authorization: <value hidden>");
		else
			ec_log_debug("< "+strBuffer);
		++n;
	} while(hr == hrSuccess);

	hr = HrParseHeaders();
	if (hr != hrSuccess)
		hr_ldebug(hr, "parsing headers failed");
	return hr;
}


HRESULT Http::HrFlushHeaders()
{
	HRESULT hr = hrSuccess;
	std::string strOutput, strConnection;
	char lpszChar[128];

	HrGetHeaderValue("Connection", &strConnection);
	// Add misc. headers
	HrResponseHeader("Server","Kopano");
	struct tm dummy;
	strftime(lpszChar, 127, "%a, %d %b %Y %H:%M:%S GMT", gmtime_safe(time(nullptr), &dummy));
	HrResponseHeader("Date", lpszChar);
	if (m_ulKeepAlive != 0 && strcasecmp(strConnection.c_str(), "keep-alive") == 0) {
		HrResponseHeader("Connection", "Keep-Alive");
		HrResponseHeader("Keep-Alive", stringify(m_ulKeepAlive));
	}
	else
	{
		HrResponseHeader("Connection", "close");
		hr = MAPI_E_END_OF_SESSION;
	}

	// create headers packet
	assert(m_ulRetCode != 0);
	if (m_ulRetCode == 0)
		HrResponseHeader(500, "Request handled incorrectly");
	ec_log_debug("> " + m_strRespHeader);
	strOutput += m_strRespHeader + "\r\n";
	m_strRespHeader.clear();
	for (const auto &h : m_lstHeaders) {
		ec_log_debug("> " + h);
		strOutput += h + "\r\n";
	}
	m_lstHeaders.clear();
	//as last line has a CRLF. The HrWriteLine adds one more CRLF.
	//this means the End of headder.
	m_lpChannel->HrWriteLine(strOutput);
	return hr;
}


HRESULT Http::HrResponseHeader(const std::string &strHeader, const std::string &strValue)
{
	m_lstHeaders.emplace_back(strHeader + ": " + strValue);
	return hrSuccess;
}


HRESULT Http::HrGetCharSet(std::string *strCharset)
{
	if (m_strCharSet.empty())
		return MAPI_E_NOT_FOUND;
	strCharset->assign(m_strCharSet);
	return hrSuccess;
}


HRESULT Http::HrFinalize()
{
	HRESULT hr = hrSuccess;

	HrResponseHeader("Content-Length", stringify(m_strRespBody.length()));

	// force chunked http for long size response, should check version >= 1.1 to disable chunking
	if (m_strRespBody.size() < HTTP_CHUNK_SIZE || m_strHttpVer != "1.1") {
		hr = HrFlushHeaders();
		if (hr != hrSuccess && hr != MAPI_E_END_OF_SESSION) {
			ec_log_debug("Http::HrFinalize flush fail %d", hr);
			m_ulRetCode = 0;
			return hr;
		}
		if (!m_strRespBody.empty()) {
			m_lpChannel->HrWriteString(m_strRespBody);
			ec_log_debug("Response body:\n%s", m_strRespBody.c_str());
		}
	}
	else
	{
		const char *lpstrBody = m_strRespBody.data();
		char lpstrLen[10];
		auto szBodyLen = m_strRespBody.size(); // length of data to be sent to the client
		size_t szBodyWritten = 0; // length of data sent to client
		unsigned int szPart = HTTP_CHUNK_SIZE;						// default length of chunk data to be written

		HrResponseHeader("Transfer-Encoding", "chunked");
		hr = HrFlushHeaders();
		if (hr != hrSuccess && hr != MAPI_E_END_OF_SESSION) {
			ec_log_debug("Http::HrFinalize flush fail(2) %d", hr);
			m_ulRetCode = 0;
			return hr;
		}

		while (szBodyWritten < szBodyLen)
		{
			if ((szBodyWritten + HTTP_CHUNK_SIZE) > szBodyLen)
				szPart = szBodyLen - szBodyWritten;				// change length of data for last chunk
			// send hex length of data and data part
			snprintf(lpstrLen, sizeof(lpstrLen), "%X", szPart);
			m_lpChannel->HrWriteLine(lpstrLen);
			m_lpChannel->HrWriteLine(string_view(lpstrBody, szPart));
			szBodyWritten += szPart;
			lpstrBody += szPart;
		}

		// end of response
		snprintf(lpstrLen, 10, "0\r\n");
		m_lpChannel->HrWriteLine(lpstrLen);
		// just the first part of the body in the log. header shows it's chunked.
		ec_log_debug("%s", m_strRespBody.c_str());
	}

	// if http_log_enable?
	char szTime[32];
	time_t now = time(NULL);
	tm local;
	std::string strAgent;
	localtime_r(&now, &local);
	// @todo we're in C LC_TIME locale to get the correct (month) format, but the timezone will be GMT, which is not wanted.
	strftime(szTime, ARRAY_SIZE(szTime), "%d/%b/%Y:%H:%M:%S %z", &local);
	HrGetHeaderValue("User-Agent", &strAgent);
	ec_log_notice("%s - %s [%s] \"%s\" %d %d \"-\" \"%s\"", m_lpChannel->peer_addr(), m_strUser.empty() ? "-" : m_strUser.c_str(), szTime, m_strAction.c_str(), m_ulRetCode, (int)m_strRespBody.length(), strAgent.c_str());
	m_ulRetCode = 0;
	return hr;
}


HRESULT Http::HrGetPass(std::wstring *strPass)
{
	if (m_strPass.empty())
		return MAPI_E_NOT_FOUND;
	return X2W(m_strPass, strPass);
}


HRESULT Http::HrRequestAuth(const std::string &strMsg)
{
	auto hr = HrResponseHeader(401, "Unauthorized");
	if (hr != hrSuccess)
		return hr;
	return HrResponseHeader("WWW-Authenticate", "Basic realm=\"" + strMsg + "\"");
}


HRESULT Http::HrGetUserAgentVersion(std::string *strUserAgentVersion)
{
	if (m_strUserAgentVersion.empty())
		return MAPI_E_NOT_FOUND;
	strUserAgentVersion -> assign(m_strUserAgentVersion);
	return hrSuccess;
}


HRESULT Http::HrSetKeepAlive(int ulKeepAlive)
{
	m_ulKeepAlive = ulKeepAlive;
	return hrSuccess;
}


HRESULT Http::HrGetUser(std::wstring *strUser)
{
	if (m_strUser.empty())
		return MAPI_E_NOT_FOUND;
	return X2W(m_strUser, strUser);
}


HRESULT Http::HrGetUrl(std::string *strUrl)
{
	if (m_strPath.empty())
		return MAPI_E_NOT_FOUND;
	strUrl->assign(urlDecode(m_strPath));
	return hrSuccess;
}


HRESULT Http::HrParseHeaders()
{
	std::string strAuthdata;
	std::string strUserAgent;

	auto items = tokenize(m_strAction, ' ', true);
	if (items.size() != 3) {
		ec_log_debug("HrParseHeaders invalid != 3 tokens");
		return MAPI_E_INVALID_PARAMETER;
	}
	m_strMethod = items[0];
	m_strURL = items[1];
	m_strHttpVer = items[2];
	// converts %20 -> ' '
	m_strPath = urlDecode(m_strURL);

	// find the content-type
	// Content-Type: text/xml;charset=UTF-8
	auto hr = HrGetHeaderValue("Content-Type", &m_strCharSet);
	if (hr == hrSuccess)
		m_strCharSet = content_type_get_charset(m_strCharSet.c_str(), m_lpConfig->GetSetting("default_charset"));
	else
		m_strCharSet = m_lpConfig->GetSetting("default_charset"); // really should be UTF-8

	hr = HrGetHeaderValue("User-Agent", &strUserAgent);
	if (hr == hrSuccess) {
		size_t space = strUserAgent.find(" ");

		if (space != std::string::npos) {
			m_strUserAgent = strUserAgent.substr(0, space);
			m_strUserAgentVersion = strUserAgent.substr(space + 1);
		}
		else {
			m_strUserAgent = strUserAgent;
		}
	}

	// find the Authorisation data (Authorization: Basic wr8y273yr2y3r87y23ry7=)
	hr = HrGetHeaderValue("Authorization", &strAuthdata);
	if (hr != hrSuccess) {
		hr = HrGetHeaderValue("WWW-Authenticate", &strAuthdata);
		if (hr != hrSuccess)
			return S_OK; /* ignore empty Authorization */
	}

	items = tokenize(strAuthdata, ' ', true);
	// we only support basic authentication
	if (items.size() != 2 || items[0] != "Basic") {
		ec_log_debug("HrParseHeaders login failed");
		return MAPI_E_LOGON_FAILED;
	}
	auto user_pass = base64_decode(items[1]);
	auto colon_pos = user_pass.find(":");
	if (colon_pos == std::string::npos) {
		ec_log_debug("HrParseHeaders password missing");
		return MAPI_E_LOGON_FAILED;
	}

	m_strUser = user_pass.substr(0, colon_pos);
	m_strPass = user_pass.substr(colon_pos+1, std::string::npos);
	return hrSuccess;
}


HRESULT Http::HrValidateReq()
{
	static const char *const lpszMethods[] = {
		"ACL", "GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS",
		"PROPFIND", "REPORT", "MKCALENDAR", "PROPPATCH", "MOVE", NULL,
	};
	bool bFound = false;

	if (m_strMethod.empty())
		return kc_perror("HTTP request method is empty", MAPI_E_INVALID_PARAMETER);
	if (!parseBool(m_lpConfig->GetSetting("enable_ical_get")) && m_strMethod == "GET") {
		ec_log_err("Denying iCalendar GET since it is disabled");
		return MAPI_E_NO_ACCESS;
	}
	for (unsigned int i = 0; lpszMethods[i] != nullptr; ++i) {
		if (m_strMethod == lpszMethods[i]) {
			bFound = true;
			break;
		}
	}

	if (!bFound)
		return hr_lerr(MAPI_E_INVALID_PARAMETER, "HTTP request \"%s\" not implemented", m_strMethod.c_str());
	// validate authentication data
	if (m_strUser.empty() || m_strPass.empty())
		// hr still success, since http request is valid
		ec_log_debug("Request missing authorization data");
	return hrSuccess;
}


HRESULT HrParseURL(const std::string &strUrl, ULONG *lpulFlag, std::string *lpstrUrlUser, std::string *lpstrFolder)
{
	HRESULT hr = hrSuccess;
	std::string strService, strFolder, strUrlUser;
	ULONG ulFlag = 0;
	auto vcUrlTokens = tokenize(strUrl, L'/', true);
	decltype(vcUrlTokens)::const_iterator iterToken;
	if (vcUrlTokens.empty())
		// root should be present, no flags are set. mostly used on OPTIONS command
		goto exit;

	if (vcUrlTokens.back().rfind(".ics") != std::string::npos)
		// Guid is retrieved using StripGuid().
		vcUrlTokens.pop_back();
	else
		// request is for folder not a calendar entry
		ulFlag |= REQ_COLLECTION;
	if (vcUrlTokens.empty())
		goto exit;
	if (vcUrlTokens.size() > 3) {
		// sub folders are not allowed
		hr = MAPI_E_TOO_COMPLEX;
		goto exit;
	}

	iterToken = vcUrlTokens.cbegin();
	//change case of Service name ICAL -> ical CALDaV ->caldav
	strService = strToLower(*iterToken++);
	if (strService == "ical")
		ulFlag |= SERVICE_ICAL;
	else if (strService == "caldav")
		ulFlag |= SERVICE_CALDAV;
	else
		ulFlag |= SERVICE_UNKNOWN;
	if (iterToken == vcUrlTokens.cend())
		goto exit;

	//change case of folder owner USER -> user, UseR -> user
	strUrlUser = strToLower(*iterToken++);
	// check if the request is for public folders and set the bool flag
	// @note: request for public folder not have user's name in the url
	if (strUrlUser == "public")
		ulFlag |= REQ_PUBLIC;
	if (iterToken == vcUrlTokens.cend())
		goto exit;
	// @todo subfolder/folder/ is not allowed! only subfolder/item.ics
	for (; iterToken != vcUrlTokens.end(); ++iterToken)
		strFolder = strFolder + *iterToken + "/";
	strFolder.erase(strFolder.length() - 1);
exit:
	if (lpulFlag)
		*lpulFlag = ulFlag;
	if (lpstrUrlUser)
		*lpstrUrlUser = std::move(strUrlUser);
	if (lpstrFolder)
		*lpstrFolder = std::move(strFolder);
	return hr;
}


HRESULT Http::HrReadBody()
{
	std::string strLength;

	// find the Content-Length
	if (HrGetHeaderValue("Content-Length", &strLength) != hrSuccess) {
		ec_log_debug("Http::HrReadBody content-length missing");
		return MAPI_E_NOT_FOUND;
	}
	auto ulContLength = atoi(strLength.c_str());
	if (ulContLength <= 0) {
		ec_log_debug("Http::HrReadBody content-length invalid %d", ulContLength);
		return MAPI_E_NOT_FOUND;
	}
	auto hr = m_lpChannel->HrReadBytes(&m_strReqBody, ulContLength);
	if (!m_strUser.empty())
		ec_log_debug("Request body:\n%s", m_strReqBody.c_str());
	return hr;
}


HRESULT Http::HrResponseHeader(unsigned int ulCode, const std::string &strResponse)
{
	m_ulRetCode = ulCode;
	// do not set headers if once set
	if (!m_strRespHeader.empty())
		return MAPI_E_CALL_FAILED;
	m_strRespHeader = "HTTP/1.1 " + stringify(ulCode) + " " + strResponse;
	return hrSuccess;
}


HRESULT Http::HrGetMethod(std::string *strMethod)
{
	if (m_strMethod.empty())
		return MAPI_E_NOT_FOUND;
	strMethod->assign(m_strMethod);
	return hrSuccess;
}


HRESULT Http::HrGetDestination(std::string *strDestination)
{
	std::string strHost, strDest;

	// example:  Host: server:port
	auto hr = HrGetHeaderValue("Host", &strHost);
	if(hr != hrSuccess) {
		ec_log_debug("Http::HrGetDestination host header missing");
		return hr;
	}
	// example:  Destination: http://server:port/caldav/username/folderid/entry.ics
	hr = HrGetHeaderValue("Destination", &strDest);
	if (hr != hrSuccess) {
		ec_log_debug("Http::HrGetDestination destination header missing");
		return hr;
	}
	auto pos = strDest.find(strHost);
	if (pos == std::string::npos) {
		ec_log_err("Refusing to move calendar item from %s to different host on url %s", strHost.c_str(), strDest.c_str());
		return MAPI_E_CALL_FAILED;
	}
	strDest.erase(0, pos + strHost.length());
	*strDestination = std::move(strDest);
	return hrSuccess;
}


HRESULT Http::HrResponseBody(const std::string &strResponse)
{
	m_strRespBody += strResponse;
	// data send in HrFinalize()
	return hrSuccess;
}


Http::Http(ECChannel *lpChannel, std::shared_ptr<ECConfig> lpConfig) :
	m_lpChannel(lpChannel), m_lpConfig(std::move(lpConfig))
{}


HRESULT Http::HrGetBody(std::string *strBody)
{
	if (m_strReqBody.empty())
		return MAPI_E_NOT_FOUND;
	strBody->assign(m_strReqBody);
	return hrSuccess;
}


HRESULT Http::X2W(const std::string &strIn, std::wstring *lpstrOut)
{
	const char *lpszCharset = (m_strCharSet.empty() ? "UTF-8" : m_strCharSet.c_str());
	return TryConvert(m_converter, strIn, rawsize(strIn), lpszCharset, *lpstrOut);
}


_tiffReadProc(thandle_t hdata, tdata_t buf, tsize_t size) {
    TIFFSTATE *state = (TIFFSTATE *)hdata;
    tsize_t to_read;

    TRACE(("_tiffReadProc: %d \n", (int)size));
    dump_state(state);

    if (state->loc > state->eof) {
        TIFFError("_tiffReadProc", "Invalid Read at loc %d, eof: %d", state->loc, state->eof);
        return 0;
    }
    to_read = min(size, min(state->size, (tsize_t)state->eof) - (tsize_t)state->loc);
    TRACE(("to_read: %d\n", (int)to_read));

    _TIFFmemcpy(buf, (UINT8 *)state->data + state->loc, to_read);
    state->loc += (toff_t)to_read;

    TRACE(("location: %u\n", (uint)state->loc));
    return to_read;
}

Fix for CVE-2021-25291

* Invalid tile boundaries lead to OOB Read in TiffDecode.c, in TiffReadRGBATile
* Check the tile validity before attempting to read.

#define uint uint32

#define fd_to_tiff_fd(fd) ((int)_get_osfhandle(fd))


ImagingLibTiffDecode(
    Imaging im, ImagingCodecState state, UINT8 *buffer, Py_ssize_t bytes) {
    TIFFSTATE *clientstate = (TIFFSTATE *)state->context;
    char *filename = "tempfile.tif";
    char *mode = "r";
    TIFF *tiff;
    uint16 photometric = 0;  // init to not PHOTOMETRIC_YCBCR
    int isYCbCr = 0;

    /* buffer is the encoded file, bytes is the length of the encoded file */
    /*     it all ends up in state->buffer, which is a uint8* from Imaging.h */

    TRACE(("in decoder: bytes %d\n", bytes));
    TRACE(
        ("State: count %d, state %d, x %d, y %d, ystep %d\n",
         state->count,
         state->state,
         state->x,
         state->y,
         state->ystep));
    TRACE(
        ("State: xsize %d, ysize %d, xoff %d, yoff %d \n",
         state->xsize,
         state->ysize,
         state->xoff,
         state->yoff));
    TRACE(("State: bits %d, bytes %d \n", state->bits, state->bytes));
    TRACE(
        ("Buffer: %p: %c%c%c%c\n",
         buffer,
         (char)buffer[0],
         (char)buffer[1],
         (char)buffer[2],
         (char)buffer[3]));
    TRACE(
        ("State->Buffer: %c%c%c%c\n",
         (char)state->buffer[0],
         (char)state->buffer[1],
         (char)state->buffer[2],
         (char)state->buffer[3]));
    TRACE(
        ("Image: mode %s, type %d, bands: %d, xsize %d, ysize %d \n",
         im->mode,
         im->type,
         im->bands,
         im->xsize,
         im->ysize));
    TRACE(
        ("Image: image8 %p, image32 %p, image %p, block %p \n",
         im->image8,
         im->image32,
         im->image,
         im->block));
    TRACE(("Image: pixelsize: %d, linesize %d \n", im->pixelsize, im->linesize));

    dump_state(clientstate);
    clientstate->size = bytes;
    clientstate->eof = clientstate->size;
    clientstate->loc = 0;
    clientstate->data = (tdata_t)buffer;
    clientstate->flrealloc = 0;
    dump_state(clientstate);

    TIFFSetWarningHandler(NULL);
    TIFFSetWarningHandlerExt(NULL);

    if (clientstate->fp) {
        TRACE(("Opening using fd: %d\n", clientstate->fp));
        lseek(clientstate->fp, 0, SEEK_SET);  // Sometimes, I get it set to the end.
        tiff = TIFFFdOpen(fd_to_tiff_fd(clientstate->fp), filename, mode);
    } else {
        TRACE(("Opening from string\n"));
        tiff = TIFFClientOpen(
            filename,
            mode,
            (thandle_t)clientstate,
            _tiffReadProc,
            _tiffWriteProc,
            _tiffSeekProc,
            _tiffCloseProc,
            _tiffSizeProc,
            _tiffMapProc,
            _tiffUnmapProc);
    }

    if (!tiff) {
        TRACE(("Error, didn't get the tiff\n"));
        state->errcode = IMAGING_CODEC_BROKEN;
        return -1;
    }

    if (clientstate->ifd) {
        int rv;
        uint32 ifdoffset = clientstate->ifd;
        TRACE(("reading tiff ifd %u\n", ifdoffset));
        rv = TIFFSetSubDirectory(tiff, ifdoffset);
        if (!rv) {
            TRACE(("error in TIFFSetSubDirectory"));
            goto decode_err;
        }
    }

    TIFFGetField(tiff, TIFFTAG_PHOTOMETRIC, &photometric);
    isYCbCr = photometric == PHOTOMETRIC_YCBCR;

    if (TIFFIsTiled(tiff)) {
        INT32 x, y, tile_y;
        UINT32 tile_width, tile_length, current_tile_length, current_line,
            current_tile_width, row_byte_size;
        UINT8 *new_data;

        TIFFGetField(tiff, TIFFTAG_TILEWIDTH, &tile_width);
        TIFFGetField(tiff, TIFFTAG_TILELENGTH, &tile_length);

        /* overflow check for row_byte_size calculation */
        if ((UINT32)INT_MAX / state->bits < tile_width) {
            state->errcode = IMAGING_CODEC_MEMORY;
            goto decode_err;
        }

        if (isYCbCr) {
            row_byte_size = tile_width * 4;
            /* sanity check, we use this value in shuffle below */
            if (im->pixelsize != 4) {
                state->errcode = IMAGING_CODEC_BROKEN;
                goto decode_err;
            }
        } else {
            // We could use TIFFTileSize, but for YCbCr data it returns subsampled data
            // size
            row_byte_size = (tile_width * state->bits + 7) / 8;
        }

        /* overflow check for realloc */
        if (INT_MAX / row_byte_size < tile_length) {
            state->errcode = IMAGING_CODEC_MEMORY;
            goto decode_err;
        }

        state->bytes = row_byte_size * tile_length;

        if (TIFFTileSize(tiff) > state->bytes) {
            // If the strip size as expected by LibTiff isn't what we're expecting,
            // abort.
            state->errcode = IMAGING_CODEC_MEMORY;
            goto decode_err;
        }

        /* realloc to fit whole tile */
        /* malloc check above */
        new_data = realloc(state->buffer, state->bytes);
        if (!new_data) {
            state->errcode = IMAGING_CODEC_MEMORY;
            goto decode_err;
        }

        state->buffer = new_data;

        TRACE(("TIFFTileSize: %d\n", state->bytes));

        for (y = state->yoff; y < state->ysize; y += tile_length) {
            for (x = state->xoff; x < state->xsize; x += tile_width) {
                /* Sanity Check. Apparently in some cases, the TiffReadRGBA* functions
                   have a different view of the size of the tiff than we're getting from
                   other functions. So, we need to check here.
                */
                if (!TIFFCheckTile(tiff, x, y, 0, 0)) {
                    TRACE(("Check Tile Error, Tile at %dx%d\n", x, y));
                    state->errcode = IMAGING_CODEC_BROKEN;
                    goto decode_err;
                }
                if (isYCbCr) {
                    /* To avoid dealing with YCbCr subsampling, let libtiff handle it */
                    if (!TIFFReadRGBATile(tiff, x, y, (UINT32 *)state->buffer)) {
                        TRACE(("Decode Error, Tile at %dx%d\n", x, y));
                        state->errcode = IMAGING_CODEC_BROKEN;
                        goto decode_err;
                    }
                } else {
                    if (TIFFReadTile(tiff, (tdata_t)state->buffer, x, y, 0, 0) == -1) {
                        TRACE(("Decode Error, Tile at %dx%d\n", x, y));
                        state->errcode = IMAGING_CODEC_BROKEN;
                        goto decode_err;
                    }
                }

                TRACE(("Read tile at %dx%d; \n\n", x, y));

                current_tile_width = min((INT32)tile_width, state->xsize - x);
                current_tile_length = min((INT32)tile_length, state->ysize - y);
                // iterate over each line in the tile and stuff data into image
                for (tile_y = 0; tile_y < current_tile_length; tile_y++) {
                    TRACE(
                        ("Writing tile data at %dx%d using tile_width: %d; \n",
                         tile_y + y,
                         x,
                         current_tile_width));

                    // UINT8 * bbb = state->buffer + tile_y * row_byte_size;
                    // TRACE(("chars: %x%x%x%x\n", ((UINT8 *)bbb)[0], ((UINT8 *)bbb)[1],
                    // ((UINT8 *)bbb)[2], ((UINT8 *)bbb)[3]));
                    /*
                     * For some reason the TIFFReadRGBATile() function
                     * chooses the lower left corner as the origin.
                     * Vertically mirror by shuffling the scanlines
                     * backwards
                     */

                    if (isYCbCr) {
                        current_line = tile_length - tile_y - 1;
                    } else {
                        current_line = tile_y;
                    }

                    state->shuffle(
                        (UINT8 *)im->image[tile_y + y] + x * im->pixelsize,
                        state->buffer + current_line * row_byte_size,
                        current_tile_width);
                }
            }
        }
    } else {
        if (!isYCbCr) {
            _decodeStrip(im, state, tiff);
        } else {
            _decodeStripYCbCr(im, state, tiff);
        }
    }

decode_err:
    TIFFClose(tiff);
    TRACE(("Done Decoding, Returning \n"));
    // Returning -1 here to force ImageFile.load to break, rather than
    // even think about looping back around.
    return -1;
}

#define uint uint32

#define fd_to_tiff_fd(fd) (fd)

#define fd_to_tiff_fd(fd) ((int)_get_osfhandle(fd))


mt76_dma_tx_cleanup(struct mt76_dev *dev, enum mt76_txq_id qid, bool flush)
{
	struct mt76_sw_queue *sq = &dev->q_tx[qid];
	struct mt76_queue *q = sq->q;
	struct mt76_queue_entry entry;
	unsigned int n_swq_queued[4] = {};
	unsigned int n_queued = 0;
	bool wake = false;
	int i, last;

	if (!q)
		return;

	if (flush)
		last = -1;
	else
		last = readl(&q->regs->dma_idx);

	while ((q->queued > n_queued) && q->tail != last) {
		mt76_dma_tx_cleanup_idx(dev, q, q->tail, &entry);
		if (entry.schedule)
			n_swq_queued[entry.qid]++;

		q->tail = (q->tail + 1) % q->ndesc;
		n_queued++;

		if (entry.skb)
			dev->drv->tx_complete_skb(dev, qid, &entry);

		if (entry.txwi) {
			if (!(dev->drv->drv_flags & MT_DRV_TXWI_NO_FREE))
				mt76_put_txwi(dev, entry.txwi);
			wake = !flush;
		}

		if (!flush && q->tail == last)
			last = readl(&q->regs->dma_idx);
	}

	spin_lock_bh(&q->lock);

	q->queued -= n_queued;
	for (i = 0; i < ARRAY_SIZE(n_swq_queued); i++) {
		if (!n_swq_queued[i])
			continue;

		dev->q_tx[i].swq_queued -= n_swq_queued[i];
	}

	if (flush)
		mt76_dma_sync_idx(dev, q);

	wake = wake && q->stopped &&
	       qid < IEEE80211_NUM_ACS && q->queued < q->ndesc - 8;
	if (wake)
		q->stopped = false;

	if (!q->queued)
		wake_up(&dev->tx_wait);

	spin_unlock_bh(&q->lock);

	if (wake)
		ieee80211_wake_queue(dev->hw, qid);
}

mt76: fix array overflow on receiving too many fragments for a packet

If the hardware receives an oversized packet with too many rx fragments,
skb_shinfo(skb)->frags can overflow and corrupt memory of adjacent pages.
This becomes especially visible if it corrupts the freelist pointer of
a slab page.

Cc: stable@vger.kernel.org
Signed-off-by: Felix Fietkau <nbd@nbd.name>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>


void mt76_dma_cleanup(struct mt76_dev *dev)
{
	int i;

	netif_napi_del(&dev->tx_napi);
	for (i = 0; i < ARRAY_SIZE(dev->q_tx); i++)
		mt76_dma_tx_cleanup(dev, i, true);

	for (i = 0; i < ARRAY_SIZE(dev->q_rx); i++) {
		netif_napi_del(&dev->napi[i]);
		mt76_dma_rx_cleanup(dev, &dev->q_rx[i]);
	}
}


mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
		     int idx, int n_desc, int bufsize,
		     u32 ring_base)
{
	int size;
	int i;

	spin_lock_init(&q->lock);

	q->regs = dev->mmio.regs + ring_base + idx * MT_RING_SIZE;
	q->ndesc = n_desc;
	q->buf_size = bufsize;
	q->hw_idx = idx;

	size = q->ndesc * sizeof(struct mt76_desc);
	q->desc = dmam_alloc_coherent(dev->dev, size, &q->desc_dma, GFP_KERNEL);
	if (!q->desc)
		return -ENOMEM;

	size = q->ndesc * sizeof(*q->entry);
	q->entry = devm_kzalloc(dev->dev, size, GFP_KERNEL);
	if (!q->entry)
		return -ENOMEM;

	/* clear descriptors */
	for (i = 0; i < q->ndesc; i++)
		q->desc[i].ctrl = cpu_to_le32(MT_DMA_CTL_DMA_DONE);

	writel(q->desc_dma, &q->regs->desc_base);
	writel(0, &q->regs->cpu_idx);
	writel(0, &q->regs->dma_idx);
	writel(q->ndesc, &q->regs->ring_size);

	return 0;
}


mt76_dma_dequeue(struct mt76_dev *dev, struct mt76_queue *q, bool flush,
		 int *len, u32 *info, bool *more)
{
	int idx = q->tail;

	*more = false;
	if (!q->queued)
		return NULL;

	if (!flush && !(q->desc[idx].ctrl & cpu_to_le32(MT_DMA_CTL_DMA_DONE)))
		return NULL;

	q->tail = (q->tail + 1) % q->ndesc;
	q->queued--;

	return mt76_dma_get_buf(dev, q, idx, len, info, more);
}


mt76_dma_init(struct mt76_dev *dev)
{
	int i;

	init_dummy_netdev(&dev->napi_dev);

	for (i = 0; i < ARRAY_SIZE(dev->q_rx); i++) {
		netif_napi_add(&dev->napi_dev, &dev->napi[i], mt76_dma_rx_poll,
			       64);
		mt76_dma_rx_fill(dev, &dev->q_rx[i]);
		skb_queue_head_init(&dev->rx_skb[i]);
		napi_enable(&dev->napi[i]);
	}

	return 0;
}


mt76_dma_add_buf(struct mt76_dev *dev, struct mt76_queue *q,
		 struct mt76_queue_buf *buf, int nbufs, u32 info,
		 struct sk_buff *skb, void *txwi)
{
	struct mt76_desc *desc;
	u32 ctrl;
	int i, idx = -1;

	if (txwi) {
		q->entry[q->head].txwi = DMA_DUMMY_DATA;
		q->entry[q->head].skip_buf0 = true;
	}

	for (i = 0; i < nbufs; i += 2, buf += 2) {
		u32 buf0 = buf[0].addr, buf1 = 0;

		ctrl = FIELD_PREP(MT_DMA_CTL_SD_LEN0, buf[0].len);
		if (i < nbufs - 1) {
			buf1 = buf[1].addr;
			ctrl |= FIELD_PREP(MT_DMA_CTL_SD_LEN1, buf[1].len);
		}

		if (i == nbufs - 1)
			ctrl |= MT_DMA_CTL_LAST_SEC0;
		else if (i == nbufs - 2)
			ctrl |= MT_DMA_CTL_LAST_SEC1;

		idx = q->head;
		q->head = (q->head + 1) % q->ndesc;

		desc = &q->desc[idx];

		WRITE_ONCE(desc->buf0, cpu_to_le32(buf0));
		WRITE_ONCE(desc->buf1, cpu_to_le32(buf1));
		WRITE_ONCE(desc->info, cpu_to_le32(info));
		WRITE_ONCE(desc->ctrl, cpu_to_le32(ctrl));

		q->queued++;
	}

	q->entry[idx].txwi = txwi;
	q->entry[idx].skb = skb;

	return idx;
}


mt76_dma_rx_poll(struct napi_struct *napi, int budget)
{
	struct mt76_dev *dev;
	int qid, done = 0, cur;

	dev = container_of(napi->dev, struct mt76_dev, napi_dev);
	qid = napi - dev->napi;

	rcu_read_lock();

	do {
		cur = mt76_dma_rx_process(dev, &dev->q_rx[qid], budget - done);
		mt76_rx_poll_complete(dev, qid, napi);
		done += cur;
	} while (cur && done < budget);

	rcu_read_unlock();

	if (done < budget && napi_complete(napi))
		dev->drv->rx_poll_complete(dev, qid);

	return done;
}


mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
		 int *len, u32 *info, bool *more)
{
	struct mt76_queue_entry *e = &q->entry[idx];
	struct mt76_desc *desc = &q->desc[idx];
	dma_addr_t buf_addr;
	void *buf = e->buf;
	int buf_len = SKB_WITH_OVERHEAD(q->buf_size);

	buf_addr = le32_to_cpu(READ_ONCE(desc->buf0));
	if (len) {
		u32 ctl = le32_to_cpu(READ_ONCE(desc->ctrl));
		*len = FIELD_GET(MT_DMA_CTL_SD_LEN0, ctl);
		*more = !(ctl & MT_DMA_CTL_LAST_SEC0);
	}

	if (info)
		*info = le32_to_cpu(desc->info);

	dma_unmap_single(dev->dev, buf_addr, buf_len, DMA_FROM_DEVICE);
	e->buf = NULL;

	return buf;
}


mt76_dma_tx_queue_skb_raw(struct mt76_dev *dev, enum mt76_txq_id qid,
			  struct sk_buff *skb, u32 tx_info)
{
	struct mt76_queue *q = dev->q_tx[qid].q;
	struct mt76_queue_buf buf;
	dma_addr_t addr;

	addr = dma_map_single(dev->dev, skb->data, skb->len,
			      DMA_TO_DEVICE);
	if (unlikely(dma_mapping_error(dev->dev, addr)))
		return -ENOMEM;

	buf.addr = addr;
	buf.len = skb->len;

	spin_lock_bh(&q->lock);
	mt76_dma_add_buf(dev, q, &buf, 1, tx_info, skb, NULL);
	mt76_dma_kick_queue(dev, q);
	spin_unlock_bh(&q->lock);

	return 0;
}


mt76_dma_kick_queue(struct mt76_dev *dev, struct mt76_queue *q)
{
	writel(q->head, &q->regs->cpu_idx);
}


mt76_dma_rx_reset(struct mt76_dev *dev, enum mt76_rxq_id qid)
{
	struct mt76_queue *q = &dev->q_rx[qid];
	int i;

	for (i = 0; i < q->ndesc; i++)
		q->desc[i].ctrl &= ~cpu_to_le32(MT_DMA_CTL_DMA_DONE);

	mt76_dma_rx_cleanup(dev, q);
	mt76_dma_sync_idx(dev, q);
	mt76_dma_rx_fill(dev, q);

	if (!q->rx_head)
		return;

	dev_kfree_skb(q->rx_head);
	q->rx_head = NULL;
}


mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q)
{
	dma_addr_t addr;
	void *buf;
	int frames = 0;
	int len = SKB_WITH_OVERHEAD(q->buf_size);
	int offset = q->buf_offset;

	spin_lock_bh(&q->lock);

	while (q->queued < q->ndesc - 1) {
		struct mt76_queue_buf qbuf;

		buf = page_frag_alloc(&q->rx_page, q->buf_size, GFP_ATOMIC);
		if (!buf)
			break;

		addr = dma_map_single(dev->dev, buf, len, DMA_FROM_DEVICE);
		if (unlikely(dma_mapping_error(dev->dev, addr))) {
			skb_free_frag(buf);
			break;
		}

		qbuf.addr = addr + offset;
		qbuf.len = len - offset;
		mt76_dma_add_buf(dev, q, &qbuf, 1, 0, buf, NULL);
		frames++;
	}

	if (frames)
		mt76_dma_kick_queue(dev, q);

	spin_unlock_bh(&q->lock);

	return frames;
}


mt76_dma_sync_idx(struct mt76_dev *dev, struct mt76_queue *q)
{
	writel(q->desc_dma, &q->regs->desc_base);
	writel(q->ndesc, &q->regs->ring_size);
	q->head = readl(&q->regs->dma_idx);
	q->tail = q->head;
	writel(q->head, &q->regs->cpu_idx);
}


mt76_dma_tx_queue_skb(struct mt76_dev *dev, enum mt76_txq_id qid,
		      struct sk_buff *skb, struct mt76_wcid *wcid,
		      struct ieee80211_sta *sta)
{
	struct mt76_queue *q = dev->q_tx[qid].q;
	struct mt76_tx_info tx_info = {
		.skb = skb,
	};
	int len, n = 0, ret = -ENOMEM;
	struct mt76_queue_entry e;
	struct mt76_txwi_cache *t;
	struct sk_buff *iter;
	dma_addr_t addr;
	u8 *txwi;

	t = mt76_get_txwi(dev);
	if (!t) {
		ieee80211_free_txskb(dev->hw, skb);
		return -ENOMEM;
	}
	txwi = mt76_get_txwi_ptr(dev, t);

	skb->prev = skb->next = NULL;
	if (dev->drv->drv_flags & MT_DRV_TX_ALIGNED4_SKBS)
		mt76_insert_hdr_pad(skb);

	len = skb_headlen(skb);
	addr = dma_map_single(dev->dev, skb->data, len, DMA_TO_DEVICE);
	if (unlikely(dma_mapping_error(dev->dev, addr)))
		goto free;

	tx_info.buf[n].addr = t->dma_addr;
	tx_info.buf[n++].len = dev->drv->txwi_size;
	tx_info.buf[n].addr = addr;
	tx_info.buf[n++].len = len;

	skb_walk_frags(skb, iter) {
		if (n == ARRAY_SIZE(tx_info.buf))
			goto unmap;

		addr = dma_map_single(dev->dev, iter->data, iter->len,
				      DMA_TO_DEVICE);
		if (unlikely(dma_mapping_error(dev->dev, addr)))
			goto unmap;

		tx_info.buf[n].addr = addr;
		tx_info.buf[n++].len = iter->len;
	}
	tx_info.nbuf = n;

	dma_sync_single_for_cpu(dev->dev, t->dma_addr, dev->drv->txwi_size,
				DMA_TO_DEVICE);
	ret = dev->drv->tx_prepare_skb(dev, txwi, qid, wcid, sta, &tx_info);
	dma_sync_single_for_device(dev->dev, t->dma_addr, dev->drv->txwi_size,
				   DMA_TO_DEVICE);
	if (ret < 0)
		goto unmap;

	if (q->queued + (tx_info.nbuf + 1) / 2 >= q->ndesc - 1) {
		ret = -ENOMEM;
		goto unmap;
	}

	return mt76_dma_add_buf(dev, q, tx_info.buf, tx_info.nbuf,
				tx_info.info, tx_info.skb, t);

unmap:
	for (n--; n > 0; n--)
		dma_unmap_single(dev->dev, tx_info.buf[n].addr,
				 tx_info.buf[n].len, DMA_TO_DEVICE);

free:
	e.skb = tx_info.skb;
	e.txwi = t;
	dev->drv->tx_complete_skb(dev, qid, &e);
	mt76_put_txwi(dev, t);
	return ret;
}


mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
{
	struct page *page;
	void *buf;
	bool more;

	spin_lock_bh(&q->lock);
	do {
		buf = mt76_dma_dequeue(dev, q, true, NULL, NULL, &more);
		if (!buf)
			break;

		skb_free_frag(buf);
	} while (1);
	spin_unlock_bh(&q->lock);

	if (!q->rx_page.va)
		return;

	page = virt_to_page(q->rx_page.va);
	__page_frag_cache_drain(page, q->rx_page.pagecnt_bias);
	memset(&q->rx_page, 0, sizeof(q->rx_page));
}


mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
{
	int len, data_len, done = 0;
	struct sk_buff *skb;
	unsigned char *data;
	bool more;

	while (done < budget) {
		u32 info;

		data = mt76_dma_dequeue(dev, q, false, &len, &info, &more);
		if (!data)
			break;

		if (q->rx_head)
			data_len = q->buf_size;
		else
			data_len = SKB_WITH_OVERHEAD(q->buf_size);

		if (data_len < len + q->buf_offset) {
			dev_kfree_skb(q->rx_head);
			q->rx_head = NULL;

			skb_free_frag(data);
			continue;
		}

		if (q->rx_head) {
			mt76_add_fragment(dev, q, data, len, more);
			continue;
		}

		skb = build_skb(data, q->buf_size);
		if (!skb) {
			skb_free_frag(data);
			continue;
		}
		skb_reserve(skb, q->buf_offset);

		if (q == &dev->q_rx[MT_RXQ_MCU]) {
			u32 *rxfce = (u32 *)skb->cb;
			*rxfce = info;
		}

		__skb_put(skb, len);
		done++;

		if (more) {
			q->rx_head = skb;
			continue;
		}

		dev->drv->rx_skb(dev, q - dev->q_rx, skb);
	}

	mt76_dma_rx_fill(dev, q);
	return done;
}


void mt76_dma_attach(struct mt76_dev *dev)
{
	dev->queue_ops = &mt76_dma_ops;
}


mt76_dma_tx_cleanup_idx(struct mt76_dev *dev, struct mt76_queue *q, int idx,
			struct mt76_queue_entry *prev_e)
{
	struct mt76_queue_entry *e = &q->entry[idx];
	__le32 __ctrl = READ_ONCE(q->desc[idx].ctrl);
	u32 ctrl = le32_to_cpu(__ctrl);

	if (!e->skip_buf0) {
		__le32 addr = READ_ONCE(q->desc[idx].buf0);
		u32 len = FIELD_GET(MT_DMA_CTL_SD_LEN0, ctrl);

		dma_unmap_single(dev->dev, le32_to_cpu(addr), len,
				 DMA_TO_DEVICE);
	}

	if (!(ctrl & MT_DMA_CTL_LAST_SEC0)) {
		__le32 addr = READ_ONCE(q->desc[idx].buf1);
		u32 len = FIELD_GET(MT_DMA_CTL_SD_LEN1, ctrl);

		dma_unmap_single(dev->dev, le32_to_cpu(addr), len,
				 DMA_TO_DEVICE);
	}

	if (e->txwi == DMA_DUMMY_DATA)
		e->txwi = NULL;

	if (e->skb == DMA_DUMMY_DATA)
		e->skb = NULL;

	*prev_e = *e;
	memset(e, 0, sizeof(*e));
}


mt76_add_fragment(struct mt76_dev *dev, struct mt76_queue *q, void *data,
		  int len, bool more)
{
	struct page *page = virt_to_head_page(data);
	int offset = data - page_address(page);
	struct sk_buff *skb = q->rx_head;
	struct skb_shared_info *shinfo = skb_shinfo(skb);

	if (shinfo->nr_frags < ARRAY_SIZE(shinfo->frags)) {
		offset += q->buf_offset;
		skb_add_rx_frag(skb, shinfo->nr_frags, page, offset, len,
				q->buf_size);
	}

	if (more)
		return;

	q->rx_head = NULL;
	dev->drv->rx_skb(dev, q - dev->q_rx, skb);
}


lytype_find(const char *module, const char *revision, const char *type_name)
{
    uint16_t u;

    for (u = 0; u < type_plugins_count; ++u) {
        if (ly_strequal(module, type_plugins[u].module, 0) && ((!revision && !type_plugins[u].revision)
                || (revision && ly_strequal(revision, type_plugins[u].revision, 0)))
                && ly_strequal(type_name, type_plugins[u].name, 0)) {
            return &(type_plugins[u]);
        }
    }

    return NULL;
}

plugins BUGFIX handle empty revision correctly

Fixes #1451

static struct lyext_plugin_list *ext_plugins = NULL;

static uint16_t ext_plugins_count = 0;

static struct lytype_plugin_list *type_plugins = NULL;

static uint16_t type_plugins_count = 0;

static struct ly_set dlhandlers = {0, 0, {NULL}};

static char **loaded_plugins = NULL;

static uint16_t loaded_plugins_count = 0;


ext_get_plugin(const char *name, const char *module, const char *revision)
{
    uint16_t u;

    assert(name);
    assert(module);

    for (u = 0; u < ext_plugins_count; u++) {
        if (!strcmp(name, ext_plugins[u].name) && !strcmp(module, ext_plugins[u].module) &&
                ((!revision && !ext_plugins[u].revision) || (revision && !strcmp(revision, ext_plugins[u].revision)))) {
            /* we have the match */
            return ext_plugins[u].plugin;
        }
    }

    /* plugin not found */
    return NULL;
}


ly_load_plugins_dir(DIR *dir, const char *dir_path, int ext_or_type)
{
    struct dirent *file;
    size_t len;
    char *str, *name;
    void *dlhandler;
    int ret;

#ifdef STATIC
    return;
#endif /* STATIC */

    while ((file = readdir(dir))) {
        /* required format of the filename is *LY_PLUGIN_SUFFIX */
        len = strlen(file->d_name);
        if (len < LY_PLUGIN_SUFFIX_LEN + 1 ||
                strcmp(&file->d_name[len - LY_PLUGIN_SUFFIX_LEN], LY_PLUGIN_SUFFIX)) {
            continue;
        }

        /* and construct the filepath */
        if (asprintf(&str, "%s/%s", dir_path, file->d_name) == -1) {
            LOGMEM(NULL);
            return;
        }

        /* load the plugin */
        dlhandler = dlopen(str, RTLD_NOW);
        if (!dlhandler) {
            LOGERR(NULL, LY_ESYS, "Loading \"%s\" as a plugin failed (%s).", str, dlerror());
            free(str);
            continue;
        }
        if (ly_set_contains(&dlhandlers, dlhandler) != -1) {
            /* the plugin is already loaded */
            LOGVRB("Plugin \"%s\" already loaded.", str);
            free(str);

            /* keep the refcount of the shared object correct */
            dlclose(dlhandler);
            continue;
        }
        dlerror();    /* Clear any existing error */

        /* store the name without the suffix */
        name = strndup(file->d_name, len - LY_PLUGIN_SUFFIX_LEN);
        if (!name) {
            LOGMEM(NULL);
            dlclose(dlhandler);
            free(str);
            return;
        }

        if (ext_or_type) {
            ret = lyext_load_plugin(dlhandler, name);
        } else {
            ret = lytype_load_plugin(dlhandler, name);
        }
        if (!ret) {
            LOGVRB("Plugin \"%s\" successfully loaded.", str);
            /* spends name */
            ly_add_loaded_plugin(name);
            /* keep the handler */
            ly_set_add(&dlhandlers, dlhandler, LY_SET_OPT_USEASLIST);
        } else {
            free(name);
            dlclose(dlhandler);
        }
        free(str);

        if (ret == -1) {
            /* finish on error */
            break;
        }
    }
}

static struct lyext_plugin_list *ext_plugins = NULL;

static struct lytype_plugin_list *type_plugins = NULL;

static struct ly_set dlhandlers = {0, 0, {NULL}};

static char **loaded_plugins = NULL;


lys_ext_instance_presence(struct lys_ext *def, struct lys_ext_instance **ext, uint8_t ext_size)
{
    FUN_IN;

    uint8_t index;

    if (!def || (ext_size && !ext)) {
        LOGARG;
        return -1;
    }

    /* search for the extension instance */
    for (index = 0; index < ext_size; index++) {
        if (ext[index]->module->ctx == def->module->ctx) {
            /* from the same context */
            if (ext[index]->def == def) {
                return index;
            }
        } else {
            /* from different contexts */
            if (ly_strequal0(ext[index]->def->name, def->name)
                    && ly_strequal0(lys_main_module(ext[index]->def->module)->name, lys_main_module(def->module)->name)) {
                return index;
            }
        }
    }

    /* not found */
    return -1;
}

static struct lyext_plugin_list *ext_plugins = NULL;

static struct lytype_plugin_list *type_plugins = NULL;

static struct ly_set dlhandlers = {0, 0, {NULL}};


lytype_free(const struct lys_type *type, lyd_val value, const char *value_str)
{
    struct lytype_plugin_list *p;
    struct lys_node_leaf sleaf;
    struct lyd_node_leaf_list leaf;
    struct lys_module *mod;

    memset(&sleaf, 0, sizeof sleaf);
    memset(&leaf, 0, sizeof leaf);

    while (type->base == LY_TYPE_LEAFREF) {
        type = &type->info.lref.target->type;
    }
    if (type->base == LY_TYPE_UNION) {
        /* create a fake schema node */
        sleaf.module = type->parent->module;
        sleaf.name = "fake-leaf";
        sleaf.type = *type;
        sleaf.nodetype = LYS_LEAF;

        /* and a fake data node */
        leaf.schema = (struct lys_node *)&sleaf;
        leaf.value = value;
        leaf.value_str = value_str;

        /* find the original type */
        type = lyd_leaf_type(&leaf);
        if (!type) {
            LOGINT(sleaf.module->ctx);
            return;
        }
    }

    mod = type->der->module;
    if (!mod) {
        LOGINT(type->parent->module->ctx);
        return;
    }

    p = lytype_find(mod->name, mod->rev_size ? mod->rev[0].date : NULL, type->der->name);
    if (!p) {
        LOGINT(mod->ctx);
        return;
    }

    if (p->free_clb) {
        p->free_clb(value.ptr);
    }
}


ly_register_types(struct lytype_plugin_list *plugin, const char *log_name)
{
    FUN_IN;

    struct lytype_plugin_list *p;
    uint32_t u, v;

    for (u = 0; plugin[u].name; u++) {
        /* check user type implementations for collisions */
        for (v = 0; v < type_plugins_count; v++) {
            if (!strcmp(plugin[u].name, type_plugins[v].name) &&
                    !strcmp(plugin[u].module, type_plugins[v].module) &&
                    (!plugin[u].revision || !type_plugins[v].revision || !strcmp(plugin[u].revision, type_plugins[v].revision))) {
                LOGERR(NULL, LY_ESYS, "Processing \"%s\" extension plugin failed,"
                        "implementation collision for extension %s from module %s%s%s.",
                        log_name, plugin[u].name, plugin[u].module, plugin[u].revision ? "@" : "",
                        plugin[u].revision ? plugin[u].revision : "");
                return 1;
            }
        }
    }

    /* add the new plugins, we have number of new plugins as u */
    p = realloc(type_plugins, (type_plugins_count + u) * sizeof *type_plugins);
    if (!p) {
        LOGMEM(NULL);
        return -1;
    }
    type_plugins = p;
    for (; u; u--) {
        memcpy(&type_plugins[type_plugins_count], &plugin[u - 1], sizeof *plugin);
        type_plugins_count++;
    }

    return 0;
}

static struct lyext_plugin_list *ext_plugins = NULL;

static struct lytype_plugin_list *type_plugins = NULL;

static uint16_t type_plugins_count = 0;

static struct ly_set dlhandlers = {0, 0, {NULL}};

static char **loaded_plugins = NULL;

static uint32_t plugin_refs;


ly_register_exts(struct lyext_plugin_list *plugin, const char *log_name)
{
    FUN_IN;

    struct lyext_plugin_list *p;
    struct lyext_plugin_complex *pluginc;
    uint32_t u, v;

    for (u = 0; plugin[u].name; u++) {
        /* check extension implementations for collisions */
        for (v = 0; v < ext_plugins_count; v++) {
            if (!strcmp(plugin[u].name, ext_plugins[v].name) &&
                    !strcmp(plugin[u].module, ext_plugins[v].module) &&
                    (!plugin[u].revision || !ext_plugins[v].revision || !strcmp(plugin[u].revision, ext_plugins[v].revision))) {
                LOGERR(NULL, LY_ESYS, "Processing \"%s\" extension plugin failed,"
                        "implementation collision for extension %s from module %s%s%s.",
                        log_name, plugin[u].name, plugin[u].module, plugin[u].revision ? "@" : "",
                        plugin[u].revision ? plugin[u].revision : "");
                return 1;
            }
        }

        /* check for valid supported substatements in case of complex extension */
        if (plugin[u].plugin->type == LYEXT_COMPLEX && ((struct lyext_plugin_complex *)plugin[u].plugin)->substmt) {
            pluginc = (struct lyext_plugin_complex *)plugin[u].plugin;
            for (v = 0; pluginc->substmt[v].stmt; v++) {
                if (pluginc->substmt[v].stmt >= LY_STMT_SUBMODULE ||
                        pluginc->substmt[v].stmt == LY_STMT_VERSION ||
                        pluginc->substmt[v].stmt == LY_STMT_YINELEM) {
                    LOGERR(NULL, LY_EINVAL,
                            "Extension plugin \"%s\" (extension %s) allows not supported extension substatement (%s)",
                            log_name, plugin[u].name, ly_stmt_str[pluginc->substmt[v].stmt]);
                    return 1;
                }
                if (pluginc->substmt[v].cardinality > LY_STMT_CARD_MAND &&
                        pluginc->substmt[v].stmt >= LY_STMT_MODIFIER &&
                        pluginc->substmt[v].stmt <= LY_STMT_STATUS) {
                    LOGERR(NULL, LY_EINVAL, "Extension plugin \"%s\" (extension %s) allows multiple instances on \"%s\" "
                           "substatement, which is not supported.",
                           log_name, plugin[u].name, ly_stmt_str[pluginc->substmt[v].stmt]);
                    return 1;
                }
            }
        }
    }

    /* add the new plugins, we have number of new plugins as u */
    p = realloc(ext_plugins, (ext_plugins_count + u) * sizeof *ext_plugins);
    if (!p) {
        LOGMEM(NULL);
        return -1;
    }
    ext_plugins = p;
    for (; u; u--) {
        memcpy(&ext_plugins[ext_plugins_count], &plugin[u - 1], sizeof *plugin);
        ext_plugins_count++;
    }

    return 0;
}

static struct lyext_plugin_list *ext_plugins = NULL;

static uint16_t ext_plugins_count = 0;

static struct lytype_plugin_list *type_plugins = NULL;

static struct ly_set dlhandlers = {0, 0, {NULL}};

static char **loaded_plugins = NULL;

static uint32_t plugin_refs;


ly_clean_plugins(void)
{
    FUN_IN;

    unsigned int u;
    int ret = EXIT_SUCCESS;

#ifdef STATIC
    /* lock the extension plugins list */
    pthread_mutex_lock(&plugins_lock);

    if(ext_plugins) {
        free(ext_plugins);
        ext_plugins = NULL;
        ext_plugins_count = 0;
    }

    if(type_plugins) {
        free(type_plugins);
        type_plugins = NULL;
        type_plugins_count = 0;
    }

    for (u = 0; u < loaded_plugins_count; ++u) {
        free(loaded_plugins[u]);
    }
    free(loaded_plugins);
    loaded_plugins = NULL;
    loaded_plugins_count = 0;

    /* unlock the global structures */
    pthread_mutex_unlock(&plugins_lock);
    return ret;
#endif /* STATIC */

    /* lock the extension plugins list */
    pthread_mutex_lock(&plugins_lock);

    if (--plugin_refs) {
        /* there is a context that may refer to the plugins, so we cannot remove them */
        ret = EXIT_FAILURE;
        goto cleanup;
    }

    if (!ext_plugins_count && !type_plugins_count) {
        /* no plugin loaded - nothing to do */
        goto cleanup;
    }

    /* clean the lists */
    free(ext_plugins);
    ext_plugins = NULL;
    ext_plugins_count = 0;

    free(type_plugins);
    type_plugins = NULL;
    type_plugins_count = 0;

    for (u = 0; u < loaded_plugins_count; ++u) {
        free(loaded_plugins[u]);
    }
    free(loaded_plugins);
    loaded_plugins = NULL;
    loaded_plugins_count = 0;

    /* close the dl handlers */
    for (u = 0; u < dlhandlers.number; u++) {
        dlclose(dlhandlers.set.g[u]);
    }
    free(dlhandlers.set.g);
    dlhandlers.set.g = NULL;
    dlhandlers.size = 0;
    dlhandlers.number = 0;

cleanup:
    /* unlock the global structures */
    pthread_mutex_unlock(&plugins_lock);

    return ret;
}

static struct lyext_plugin_list *ext_plugins = NULL;

static uint16_t ext_plugins_count = 0;

static struct lytype_plugin_list *type_plugins = NULL;

static uint16_t type_plugins_count = 0;

static struct ly_set dlhandlers = {0, 0, {NULL}};

static pthread_mutex_t plugins_lock = PTHREAD_MUTEX_INITIALIZER;

static char **loaded_plugins = NULL;

static uint16_t loaded_plugins_count = 0;

static uint32_t plugin_refs;


lytype_store(const struct lys_module *mod, const char *type_name, const char **value_str, lyd_val *value)
{
    struct lytype_plugin_list *p;
    char *err_msg = NULL;

    assert(mod && type_name && value_str && value);

    p = lytype_find(mod->name, mod->rev_size ? mod->rev[0].date : NULL, type_name);
    if (p) {
        if (p->store_clb(mod->ctx, type_name, value_str, value, &err_msg)) {
            if (!err_msg) {
                if (asprintf(&err_msg, "Failed to store value \"%s\" of user type \"%s\".", *value_str, type_name) == -1) {
                    LOGMEM(mod->ctx);
                    return -1;
                }
            }
            LOGERR(mod->ctx, LY_EPLUGIN, err_msg);
            free(err_msg);
            return -1;
        }

        /* value successfully stored */
        return 0;
    }

    return 1;
}


ly_add_loaded_plugin(char *name)
{
    loaded_plugins = ly_realloc(loaded_plugins, (loaded_plugins_count + 2) * sizeof *loaded_plugins);
    LY_CHECK_ERR_RETURN(!loaded_plugins, free(name); LOGMEM(NULL), );
    ++loaded_plugins_count;

    loaded_plugins[loaded_plugins_count - 1] = name;
    loaded_plugins[loaded_plugins_count] = NULL;
}

static struct lyext_plugin_list *ext_plugins = NULL;

static struct lytype_plugin_list *type_plugins = NULL;

static struct ly_set dlhandlers = {0, 0, {NULL}};

static char **loaded_plugins = NULL;

static uint16_t loaded_plugins_count = 0;