3080 lines · 78.4 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0#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/timer.h>#include <linux/usb.h> #define SIMPLE_IO_TIMEOUT	10000	/* in milliseconds */ /*-------------------------------------------------------------------------*/ static int override_alt = -1;module_param_named(alt, override_alt, int, 0644);MODULE_PARM_DESC(alt, ">= 0 to override altsetting selection");static void complicated_callback(struct urb *urb); /*-------------------------------------------------------------------------*/ /* FIXME make these public somewhere; usbdevfs.h? */ /* Parameter for usbtest driver. */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;}; /* * Compat parameter to the usbtest driver. * This supports older user space binaries compiled with 64 bit compiler. */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;}; /* IOCTL interface to the driver. */#define USBTEST_REQUEST_32    _IOWR('U', 100, struct usbtest_param_32)/* COMPAT IOCTL interface to the driver. */#define USBTEST_REQUEST_64    _IOWR('U', 100, struct usbtest_param_64) /*-------------------------------------------------------------------------*/ #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 */	unsigned		intr:1;		/* try interrupt 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;	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;}; 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#define MAX_SGLEN	128 /*-------------------------------------------------------------------------*/ static inline void endpoint_update(int edi,				   struct usb_host_endpoint **in,				   struct usb_host_endpoint **out,				   struct usb_host_endpoint *e){	if (edi) {		if (!*in)			*in = e;	} else {		if (!*out)			*out = e;	}} 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_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;} /*-------------------------------------------------------------------------*/ /* 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,	u8			bInterval,	usb_complete_t		complete_fn){	struct urb		*urb; 	urb = usb_alloc_urb(0, GFP_KERNEL);	if (!urb)		return urb; 	if (bInterval)		usb_fill_int_urb(urb, udev, pipe, NULL, bytes, complete_fn,				NULL, bInterval);	else		usb_fill_bulk_urb(urb, udev, pipe, NULL, bytes, complete_fn,				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 ((bytes + offset) == 0)		return urb; 	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,	u8			bInterval){	return usbtest_alloc_urb(udev, pipe, bytes, URB_NO_TRANSFER_DMA_MAP, 0,			bInterval, simple_callback);} 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);} 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 unsigned get_maxpacket(struct usb_device *udev, int pipe){	struct usb_host_endpoint	*ep; 	ep = usb_pipe_endpoint(udev, pipe);	return le16_to_cpup(&ep->desc.wMaxPacketSize);} static int ss_isoc_get_packet_num(struct usb_device *udev, int pipe){	struct usb_host_endpoint *ep = usb_pipe_endpoint(udev, pipe); 	return USB_SS_MULT(ep->ss_ep_comp.bmAttributes)		* (1 + ep->ss_ep_comp.bMaxBurst);} static void simple_fill_buf(struct urb *urb){	unsigned	i;	u8		*buf = urb->transfer_buffer;	unsigned	len = urb->transfer_buffer_length;	unsigned	maxpacket; 	switch (pattern) {	default:		fallthrough;	case 0:		memset(buf, 0, len);		break;	case 1:			/* mod63 */		maxpacket = get_maxpacket(urb->dev, urb->pipe);		for (i = 0; i < len; i++)			*buf++ = (u8) ((i % maxpacket) % 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;	unsigned	maxpacket = get_maxpacket(urb->dev, urb->pipe); 	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 % maxpacket) % 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;	unsigned long		expire; 	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; 		expire = msecs_to_jiffies(SIMPLE_IO_TIMEOUT);		if (!wait_for_completion_timeout(&completion, expire)) {			usb_kill_urb(urb);			retval = (urb->status == -ENOENT ?				  -ETIMEDOUT : urb->status);		} else {			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;

Lines 1–500 of 3080.