USB: usbtest - Add tests to ensure HCDs can accept byte aligned buffers.
Add a set of new tests similar to the existing ones but using transfer buffers at an "odd" address [ie offset of +1 from the buffer obtained by kmalloc() or usb_alloc_coherent()] The new tests are: #17 : bulk out (like #1) using kmalloc and DMA mapping by USB core. #18 : bulk in (like #2) using kmalloc and DMA mapping by USB core. #19 : bulk out (like #1) using usb_alloc_coherent() #20 : bulk in (like #2) using usb_alloc_coherent() #21 : control write (like #14) #22 : isochonous out (like #15) #23 : isochonous in (like #16) Signed-off-by: Martin Fuzzey <mfuzzey@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
Родитель
50a6cb932d
Коммит
084fb206a9
|
@ -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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче