⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 usbtest.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 4 页
字号:
#include <linux/config.h>#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/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 semaphore	sem;#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 xprintk(tdev,level,fmt,args...) \	dev_printk(level ,  &(tdev)->intf->dev ,  fmt ,  ## args)#ifdef DEBUG#define DBG(dev,fmt,args...) \	xprintk(dev , KERN_DEBUG , fmt , ## args)#else#define DBG(dev,fmt,args...) \	do { } while (0)#endif /* DEBUG */#ifdef VERBOSE#define VDBG DBG#else#define VDBG(dev,fmt,args...) \	do { } while (0)#endif	/* VERBOSE */#define ERROR(dev,fmt,args...) \	xprintk(dev , KERN_ERR , fmt , ## args)#define WARN(dev,fmt,args...) \	xprintk(dev , KERN_WARNING , fmt , ## args)#define INFO(dev,fmt,args...) \	xprintk(dev , KERN_INFO , fmt , ## args)/*-------------------------------------------------------------------------*/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 altsetttings.		 */		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 (e->desc.bEndpointAddress & USB_DIR_IN) {				if (!in)					in = e;			} else {				if (!out)					out = e;			}			continue;try_iso:			if (e->desc.bEndpointAddress & USB_DIR_IN) {				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);		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, struct pt_regs *regs){	complete ((struct completion *) urb->context);}static struct urb *simple_alloc_urb (	struct usb_device	*udev,	int			pipe,	unsigned long		bytes){	struct urb		*urb;	if (bytes < 0)		return NULL;	urb = usb_alloc_urb (0, SLAB_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 = URB_NO_TRANSFER_DMA_MAP;	if (usb_pipein (pipe))		urb->transfer_flags |= URB_SHORT_NOT_OK;	urb->transfer_buffer = usb_buffer_alloc (udev, bytes, SLAB_KERNEL,			&urb->transfer_dma);	if (!urb->transfer_buffer) {		usb_free_urb (urb);		urb = NULL;	} else		memset (urb->transfer_buffer, 0, bytes);	return urb;}static unsigned pattern = 0;module_param (pattern, uint, S_IRUGO);// MODULE_PARM_DESC (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 int simple_check_buf (struct urb *urb){	unsigned	i;	u8		expected;	u8		*buf = urb->transfer_buffer;	unsigned	len = urb->actual_length;	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;		dbg ("buf[%d] = %d (not %d)", i, *buf, expected);		return -EINVAL;	}	return 0;}static void simple_free_urb (struct urb *urb){	usb_buffer_free (urb->dev, urb->transfer_buffer_length,			urb->transfer_buffer, urb->transfer_dma);	usb_free_urb (urb);}static int simple_io (	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);		if ((retval = usb_submit_urb (urb, SLAB_KERNEL)) != 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 (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_dbg (&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 [i].page)			continue;		kfree (page_address (sg [i].page) + sg [i].offset);	}	kfree (sg);}static struct scatterlist *alloc_sglist (int nents, int max, int vary){	struct scatterlist	*sg;	unsigned		i;	unsigned		size = max;	sg = kmalloc (nents * sizeof *sg, SLAB_KERNEL);	if (!sg)		return NULL;	for (i = 0; i < nents; i++) {		char		*buf;		buf = kmalloc (size, SLAB_KERNEL);		if (!buf) {			free_sglist (sg, i);			return NULL;		}		memset (buf, 0, size);		/* kmalloc pages are always physically contiguous! */		sg_init_one(&sg[i], buf, size);		if (vary) {			size += vary;			size %= max;			if (size == 0)				size = (vary < max) ? vary : max;		}	}	return sg;}static int perform_sglist (	struct usb_device	*udev,	unsigned		iterations,	int			pipe,	struct usb_sg_request	*req,	struct scatterlist	*sg,	int			nents){	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, SLAB_KERNEL);				if (retval)			break;		usb_sg_wait (req);		retval = req->status;		/* 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)		dbg ("perform_sglist failed, iterations left %d, status %d",				iterations, retval);	return retval;}/*-------------------------------------------------------------------------*//* unqueued control message testing * * there's a nice set of device functional requirements in chapter 9 of the * usb 2.0 spec, which we can apply to ANY device, even ones that don't use * special test firmware. * * we know the device is configured (or suspended) by the time it's visible * through usbfs.  we can't change that, so we won't test enumeration (which * worked 'well enough' to get here, this time), power management (ditto), * or remote wakeup (which needs human interaction). */static unsigned realworld = 1;module_param (realworld, uint, 0);MODULE_PARM_DESC (realworld, "clear to demand stricter spec compliance");static int get_altsetting (struct usbtest_dev *dev){	struct usb_interface	*iface = dev->intf;	struct usb_device	*udev = interface_to_usbdev (iface);	int			retval;	retval = usb_control_msg (udev, usb_rcvctrlpipe (udev, 0),			USB_REQ_GET_INTERFACE, USB_DIR_IN|USB_RECIP_INTERFACE,			0, iface->altsetting [0].desc.bInterfaceNumber,			dev->buf, 1, USB_CTRL_GET_TIMEOUT);	switch (retval) {	case 1:		return dev->buf [0];	case 0:		retval = -ERANGE;		// FALLTHROUGH	default:		return retval;	}}static int set_altsetting (struct usbtest_dev *dev, int alternate){	struct usb_interface		*iface = dev->intf;	struct usb_device		*udev;	if (alternate < 0 || alternate >= 256)		return -EINVAL;	udev = interface_to_usbdev (iface);	return usb_set_interface (udev,			iface->altsetting [0].desc.bInterfaceNumber,			alternate);}static int is_good_config (char *buf, int len){	struct usb_config_descriptor	*config;		if (len < sizeof *config)		return 0;	config = (struct usb_config_descriptor *) buf;	switch (config->bDescriptorType) {	case USB_DT_CONFIG:	case USB_DT_OTHER_SPEED_CONFIG:		if (config->bLength != 9) {			dbg ("bogus config descriptor length");			return 0;		}		/* this bit 'must be 1' but often isn't */		if (!realworld && !(config->bmAttributes & 0x80)) {			dbg ("high bit of config attributes not set");			return 0;		}		if (config->bmAttributes & 0x1f) {	/* reserved == 0 */			dbg ("reserved config bits set");			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;	dbg ("bogus config descriptor read size");	return 0;}/* sanity test for standard requests working with usb_control_mesg() and some * of the utility functions which use it. * * this doesn't test how endpoint halts behave or data toggles get set, since * we won't do I/O to bulk/interrupt endpoints here (which is how to change * halt or toggle).  toggle testing is impractical without support from hcds. * * this avoids failing devices linux would normally work with, by not testing * config/altsetting operations for devices that only support their defaults. * such devices rarely support those needless operations. *

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -