diff options
Diffstat (limited to 'drivers/usb/misc/usbtest.c')
| -rw-r--r-- | drivers/usb/misc/usbtest.c | 236 | 
1 files changed, 212 insertions, 24 deletions
diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index a35b427c0ba..388cc128072 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -83,6 +83,8 @@ static struct usb_device *testdev_to_usbdev(struct usbtest_dev *test)  #define WARNING(tdev, fmt, args...) \  	dev_warn(&(tdev)->intf->dev , fmt , ## args) +#define GUARD_BYTE	0xA5 +  /*-------------------------------------------------------------------------*/  static int @@ -186,11 +188,12 @@ static void simple_callback(struct urb *urb)  	complete(urb->context);  } -static struct urb *simple_alloc_urb( +static struct urb *usbtest_alloc_urb(  	struct usb_device	*udev,  	int			pipe, -	unsigned long		bytes -) +	unsigned long		bytes, +	unsigned		transfer_flags, +	unsigned		offset)  {  	struct urb		*urb; @@ -201,19 +204,46 @@ static struct urb *simple_alloc_urb(  	urb->interval = (udev->speed == USB_SPEED_HIGH)  			? (INTERRUPT_RATE << 3)  			: INTERRUPT_RATE; -	urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; +	urb->transfer_flags = transfer_flags;  	if (usb_pipein(pipe))  		urb->transfer_flags |= URB_SHORT_NOT_OK; -	urb->transfer_buffer = usb_alloc_coherent(udev, bytes, GFP_KERNEL, -			&urb->transfer_dma); + +	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); -		urb = NULL; -	} else -		memset(urb->transfer_buffer, 0, bytes); +		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); @@ -238,13 +268,38 @@ static inline void simple_fill_buf(struct urb *urb)  	}  } -static inline int simple_check_buf(struct usbtest_dev *tdev, struct urb *urb) +static inline unsigned buffer_offset(void *buf) +{ +	return (unsigned)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 */ @@ -274,8 +329,16 @@ static inline int simple_check_buf(struct usbtest_dev *tdev, struct urb *urb)  static void simple_free_urb(struct urb *urb)  { -	usb_free_coherent(urb->dev, urb->transfer_buffer_length, -			  urb->transfer_buffer, urb->transfer_dma); +	unsigned 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);  } @@ -1256,7 +1319,7 @@ done:   * try whatever we're told to try.   */  static int ctrl_out(struct usbtest_dev *dev, -		unsigned count, unsigned length, unsigned vary) +		unsigned count, unsigned length, unsigned vary, unsigned offset)  {  	unsigned		i, j, len;  	int			retval; @@ -1267,10 +1330,11 @@ static int ctrl_out(struct usbtest_dev *dev,  	if (length < 1 || length > 0xffff || vary >= length)  		return -EINVAL; -	buf = kmalloc(length, GFP_KERNEL); +	buf = kmalloc(length + offset, GFP_KERNEL);  	if (!buf)  		return -ENOMEM; +	buf += offset;  	udev = testdev_to_usbdev(dev);  	len = length;  	retval = 0; @@ -1337,7 +1401,7 @@ static int ctrl_out(struct usbtest_dev *dev,  		ERROR(dev, "ctrl_out %s failed, code %d, count %d\n",  			what, retval, i); -	kfree(buf); +	kfree(buf - offset);  	return retval;  } @@ -1373,6 +1437,8 @@ static void iso_callback(struct urb *urb)  		ctx->errors += urb->number_of_packets;  	else if (urb->actual_length != urb->transfer_buffer_length)  		ctx->errors++; +	else if (check_guard_bytes(ctx->dev, urb) != 0) +		ctx->errors++;  	if (urb->status == 0 && ctx->count > (ctx->pending - 1)  			&& !ctx->submit_error) { @@ -1408,7 +1474,8 @@ static struct urb *iso_alloc_urb(  	struct usb_device	*udev,  	int			pipe,  	struct usb_endpoint_descriptor	*desc, -	long			bytes +	long			bytes, +	unsigned offset  )  {  	struct urb		*urb; @@ -1428,13 +1495,24 @@ static struct urb *iso_alloc_urb(  	urb->number_of_packets = packets;  	urb->transfer_buffer_length = bytes; -	urb->transfer_buffer = usb_alloc_coherent(udev, bytes, GFP_KERNEL, -			&urb->transfer_dma); +	urb->transfer_buffer = usb_alloc_coherent(udev, bytes + offset, +							GFP_KERNEL, +							&urb->transfer_dma);  	if (!urb->transfer_buffer) {  		usb_free_urb(urb);  		return NULL;  	} -	memset(urb->transfer_buffer, 0, bytes); +	if (offset) { +		memset(urb->transfer_buffer, GUARD_BYTE, offset); +		urb->transfer_buffer += offset; +		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); +  	for (i = 0; i < packets; i++) {  		/* here, only the last packet will be short */  		urb->iso_frame_desc[i].length = min((unsigned) bytes, maxp); @@ -1452,7 +1530,7 @@ static struct urb *iso_alloc_urb(  static int  test_iso_queue(struct usbtest_dev *dev, struct usbtest_param *param, -		int pipe, struct usb_endpoint_descriptor *desc) +		int pipe, struct usb_endpoint_descriptor *desc, unsigned offset)  {  	struct iso_context	context;  	struct usb_device	*udev; @@ -1480,7 +1558,7 @@ test_iso_queue(struct usbtest_dev *dev, struct usbtest_param *param,  	for (i = 0; i < param->sglen; i++) {  		urbs[i] = iso_alloc_urb(udev, pipe, desc, -				param->length); +					param->length, offset);  		if (!urbs[i]) {  			status = -ENOMEM;  			goto fail; @@ -1542,6 +1620,26 @@ fail:  	return status;  } +static int test_unaligned_bulk( +	struct usbtest_dev *tdev, +	int pipe, +	unsigned length, +	int iterations, +	unsigned transfer_flags, +	const char *label) +{ +	int retval; +	struct urb *urb = usbtest_alloc_urb( +		testdev_to_usbdev(tdev), pipe, length, transfer_flags, 1); + +	if (!urb) +		return -ENOMEM; + +	retval = simple_io(tdev, urb, iterations, 0, 0, label); +	simple_free_urb(urb); +	return retval; +} +  /*-------------------------------------------------------------------------*/  /* We only have this one interface to user space, through usbfs. @@ -1843,7 +1941,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)  				realworld ? 1 : 0, param->length,  				param->vary);  		retval = ctrl_out(dev, param->iterations, -				param->length, param->vary); +				param->length, param->vary, 0);  		break;  	/* iso write tests */ @@ -1856,7 +1954,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)  				param->sglen, param->length);  		/* FIRMWARE:  iso sink */  		retval = test_iso_queue(dev, param, -				dev->out_iso_pipe, dev->iso_out); +				dev->out_iso_pipe, dev->iso_out, 0);  		break;  	/* iso read tests */ @@ -1869,13 +1967,103 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)  				param->sglen, param->length);  		/* FIRMWARE:  iso source */  		retval = test_iso_queue(dev, param, -				dev->in_iso_pipe, dev->iso_in); +				dev->in_iso_pipe, dev->iso_in, 0);  		break;  	/* FIXME unlink from queue (ring with N urbs) */  	/* 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_iso_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_iso_queue(dev, param, +				dev->in_iso_pipe, dev->iso_in, 1); +		break; +  	}  	do_gettimeofday(¶m->duration);  	param->duration.tv_sec -= start.tv_sec;  |