2529 lines · 64.8 KB · C
RawDownload
#include <linux/kernel.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/mm.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/scatterlist.h>#include <linux/mutex.h> #include <linux/usb.h>  /*-------------------------------------------------------------------------*/ /* FIXME make these public somewhere; usbdevfs.h? */struct usbtest_param {	/* inputs */	unsigned		test_num;	/* 0..(TEST_CASES-1) */	unsigned		iterations;	unsigned		length;	unsigned		vary;	unsigned		sglen; 	/* outputs */	struct timeval		duration;};#define USBTEST_REQUEST	_IOWR('U', 100, struct usbtest_param) /*-------------------------------------------------------------------------*/ #define	GENERIC		/* let probe() bind using module params */ /* Some devices that can be used for testing will have "real" drivers. * Entries for those need to be enabled here by hand, after disabling * that "real" driver. *///#define	IBOT2		/* grab iBOT2 webcams *///#define	KEYSPAN_19Qi	/* grab un-renumerated serial adapter */ /*-------------------------------------------------------------------------*/ 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 */	int			alt;}; /* this is accessed only through usbfs ioctl calls. * one ioctl to issue a test ... one lock per device. * tests create other threads if they need them. * urbs and buffers are allocated dynamically, * and data generated deterministically. */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;	struct usb_endpoint_descriptor	*iso_in, *iso_out;	struct mutex		lock; #define TBUF_SIZE	256	u8			*buf;}; static struct usb_device *testdev_to_usbdev(struct usbtest_dev *test){	return interface_to_usbdev(test->intf);} /* set up all urbs so they can be used with either bulk or interrupt */#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 GUARD_BYTE	0xA5 /*-------------------------------------------------------------------------*/ static intget_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_device		*udev; 	for (tmp = 0; tmp < intf->num_altsetting; tmp++) {		unsigned	ep; 		in = out = NULL;		iso_in = iso_out = NULL;		alt = intf->altsetting + tmp; 		/* 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; 			e = alt->endpoint + ep;			switch (e->desc.bmAttributes) {			case USB_ENDPOINT_XFER_BULK:				break;			case USB_ENDPOINT_XFER_ISOC:				if (dev->info->iso)					goto try_iso;				/* FALLTHROUGH */			default:				continue;			}			if (usb_endpoint_dir_in(&e->desc)) {				if (!in)					in = e;			} else {				if (!out)					out = e;			}			continue;try_iso:			if (usb_endpoint_dir_in(&e->desc)) {				if (!iso_in)					iso_in = e;			} else {				if (!iso_out)					iso_out = e;			}		}		if ((in && out)  ||  iso_in || iso_out)			goto found;	}	return -EINVAL; found:	udev = testdev_to_usbdev(dev);	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);		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);	}	return 0;} /*-------------------------------------------------------------------------*/ /* Support for testing basic non-queued I/O streams. * * These just package urbs as requests that can be easily canceled. * Each urb's data buffer is dynamically allocated; callers can fill * them with non-zero test data (or test for it) when appropriate. */ static void simple_callback(struct urb *urb){	complete(urb->context);} static struct urb *usbtest_alloc_urb(	struct usb_device	*udev,	int			pipe,	unsigned long		bytes,	unsigned		transfer_flags,	unsigned		offset){	struct urb		*urb; 	urb = usb_alloc_urb(0, GFP_KERNEL);	if (!urb)		return urb;	usb_fill_bulk_urb(urb, udev, pipe, NULL, bytes, simple_callback, NULL);	urb->interval = (udev->speed == USB_SPEED_HIGH)			? (INTERRUPT_RATE << 3)			: INTERRUPT_RATE;	urb->transfer_flags = transfer_flags;	if (usb_pipein(pipe))		urb->transfer_flags |= URB_SHORT_NOT_OK; 	if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)		urb->transfer_buffer = usb_alloc_coherent(udev, bytes + offset,			GFP_KERNEL, &urb->transfer_dma);	else		urb->transfer_buffer = kmalloc(bytes + offset, GFP_KERNEL); 	if (!urb->transfer_buffer) {		usb_free_urb(urb);		return NULL;	} 	/* To test unaligned transfers add an offset and fill the		unused memory with a guard value */	if (offset) {		memset(urb->transfer_buffer, GUARD_BYTE, offset);		urb->transfer_buffer += offset;		if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)			urb->transfer_dma += offset;	} 	/* For inbound transfers use guard byte so that test fails if		data not correctly copied */	memset(urb->transfer_buffer,			usb_pipein(urb->pipe) ? GUARD_BYTE : 0,			bytes);	return urb;} static struct urb *simple_alloc_urb(	struct usb_device	*udev,	int			pipe,	unsigned long		bytes){	return usbtest_alloc_urb(udev, pipe, bytes, URB_NO_TRANSFER_DMA_MAP, 0);} static unsigned pattern;static unsigned mod_pattern;module_param_named(pattern, mod_pattern, uint, S_IRUGO | S_IWUSR);MODULE_PARM_DESC(mod_pattern, "i/o pattern (0 == zeroes)"); static inline void simple_fill_buf(struct urb *urb){	unsigned	i;	u8		*buf = urb->transfer_buffer;	unsigned	len = urb->transfer_buffer_length; 	switch (pattern) {	default:		/* FALLTHROUGH */	case 0:		memset(buf, 0, len);		break;	case 1:			/* mod63 */		for (i = 0; i < len; i++)			*buf++ = (u8) (i % 63);		break;	}} static inline unsigned long buffer_offset(void *buf){	return (unsigned long)buf & (ARCH_KMALLOC_MINALIGN - 1);} static int check_guard_bytes(struct usbtest_dev *tdev, struct urb *urb){	u8 *buf = urb->transfer_buffer;	u8 *guard = buf - buffer_offset(buf);	unsigned i; 	for (i = 0; guard < buf; i++, guard++) {		if (*guard != GUARD_BYTE) {			ERROR(tdev, "guard byte[%d] %d (not %d)\n",				i, *guard, GUARD_BYTE);			return -EINVAL;		}	}	return 0;} static int simple_check_buf(struct usbtest_dev *tdev, struct urb *urb){	unsigned	i;	u8		expected;	u8		*buf = urb->transfer_buffer;	unsigned	len = urb->actual_length; 	int ret = check_guard_bytes(tdev, urb);	if (ret)		return ret; 	for (i = 0; i < len; i++, buf++) {		switch (pattern) {		/* all-zeroes has no synchronization issues */		case 0:			expected = 0;			break;		/* mod63 stays in sync with short-terminated transfers,		 * or otherwise when host and gadget agree on how large		 * each usb transfer request should be.  resync is done		 * with set_interface or set_config.		 */		case 1:			/* mod63 */			expected = i % 63;			break;		/* always fail unsupported patterns */		default:			expected = !*buf;			break;		}		if (*buf == expected)			continue;		ERROR(tdev, "buf[%d] = %d (not %d)\n", i, *buf, expected);		return -EINVAL;	}	return 0;} static void simple_free_urb(struct urb *urb){	unsigned long offset = buffer_offset(urb->transfer_buffer); 	if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)		usb_free_coherent(			urb->dev,			urb->transfer_buffer_length + offset,			urb->transfer_buffer - offset,			urb->transfer_dma - offset);	else		kfree(urb->transfer_buffer - offset);	usb_free_urb(urb);} static int simple_io(	struct usbtest_dev	*tdev,	struct urb		*urb,	int			iterations,	int			vary,	int			expected,	const char		*label){	struct usb_device	*udev = urb->dev;	int			max = urb->transfer_buffer_length;	struct completion	completion;	int			retval = 0; 	urb->context = &completion;	while (retval == 0 && iterations-- > 0) {		init_completion(&completion);		if (usb_pipeout(urb->pipe)) {			simple_fill_buf(urb);			urb->transfer_flags |= URB_ZERO_PACKET;		}		retval = usb_submit_urb(urb, GFP_KERNEL);		if (retval != 0)			break; 		/* NOTE:  no timeouts; can't be broken out of by interrupt */		wait_for_completion(&completion);		retval = urb->status;		urb->dev = udev;		if (retval == 0 && usb_pipein(urb->pipe))			retval = simple_check_buf(tdev, urb); 		if (vary) {			int	len = urb->transfer_buffer_length; 			len += vary;			len %= max;			if (len == 0)				len = (vary < max) ? vary : max;			urb->transfer_buffer_length = len;		} 		/* FIXME if endpoint halted, clear halt (and log) */	}	urb->transfer_buffer_length = max; 	if (expected != retval)		dev_err(&udev->dev,			"%s failed, iterations left %d, status %d (not %d)\n",				label, iterations, retval, expected);	return retval;}  /*-------------------------------------------------------------------------*/ /* We use scatterlist primitives to test queued I/O. * Yes, this also tests the scatterlist primitives. */ 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 struct scatterlist *alloc_sglist(int nents, int max, int vary){	struct scatterlist	*sg;	unsigned		i;	unsigned		size = max; 	sg = kmalloc_array(nents, sizeof *sg, GFP_KERNEL);	if (!sg)		return NULL;	sg_init_table(sg, nents); 	for (i = 0; i < nents; i++) {		char		*buf;		unsigned	j; 		buf = kzalloc(size, GFP_KERNEL);		if (!buf) {			free_sglist(sg, i);			return NULL;		} 		/* kmalloc pages are always physically contiguous! */		sg_set_buf(&sg[i], buf, size); 		switch (pattern) {		case 0:			/* already zeroed */			break;		case 1:			for (j = 0; j < size; j++)				*buf++ = (u8) (j % 63);			break;		} 		if (vary) {			size += vary;			size %= max;			if (size == 0)				size = (vary < max) ? vary : max;		}	} 	return sg;} static int perform_sglist(	struct usbtest_dev	*tdev,	unsigned		iterations,	int			pipe,	struct usb_sg_request	*req,	struct scatterlist	*sg,	int			nents){	struct usb_device	*udev = testdev_to_usbdev(tdev);	int			retval = 0; 	while (retval == 0 && iterations-- > 0) {		retval = usb_sg_init(req, udev, pipe,				(udev->speed == USB_SPEED_HIGH)					? (INTERRUPT_RATE << 3)					: INTERRUPT_RATE,				sg, nents, 0, GFP_KERNEL); 		if (retval)			break;		usb_sg_wait(req);		retval = req->status; 		/* FIXME check resulting data pattern */ 		/* FIXME if endpoint halted, clear halt (and log) */	} 	/* FIXME for unlink or fault handling tests, don't report	 * failure if retval is as we expected ...	 */	if (retval)		ERROR(tdev, "perform_sglist failed, "				"iterations left %d, status %d\n",				iterations, retval);

Lines 1–500 of 2529.