drivers/dma/Kconfig | 8 ++ drivers/dma/Makefile | 1 + drivers/dma/dmatest.c | 234 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 243 insertions(+), 0 deletions(-) create mode 100644 drivers/dma/dmatest.c diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 010e92e..6fe8d7a 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -23,6 +23,14 @@ config NET_DMA Since this is the main user of the DMA engine, it should be enabled; say Y here. +config DMATEST + tristate "DMA Test client" + depends on DMA_ENGINE + default n + ---help--- + Simple DMA test client. Say N unless you're debugging a + DMA Device driver. + comment "DMA Devices" config INTEL_IOATDMA diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 496cb50..127b727 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_DMA_ENGINE) += dmaengine.o obj-$(CONFIG_NET_DMA) += iovlock.o obj-$(CONFIG_INTEL_IOATDMA) += ioatdma.o obj-$(CONFIG_DW_DMAC) += dw_dmac.o +obj-$(CONFIG_DMATEST) += dmatest.o diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c new file mode 100644 index 0000000..898e2b8 --- /dev/null +++ b/drivers/dma/dmatest.c @@ -0,0 +1,234 @@ +/* + * DMA Engine test module + * + * Copyright (C) 2007 Atmel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include + +#define TEST_BUF_SIZE (16384) + +#define SRC_PATTERN 0x7c +#define SRC_PATTERN_OUTSIDE 0X8d +#define POISON_UNINIT 0x49 +#define POISON_OUTSIDE 0x37 + +struct dmatest { + struct dma_client *client; + struct task_struct *thread; + wait_queue_head_t wq; + u8 *srcbuf; + u8 *dstbuf; +}; +static struct dmatest dmatest_data = { + .wq = __WAIT_QUEUE_HEAD_INITIALIZER(dmatest_data.wq), +}; + +static void dmatest_event(struct dma_client *client, + struct dma_chan *chan, enum dma_event event) +{ + if (event == DMA_RESOURCE_ADDED) + wake_up_interruptible(&dmatest_data.wq); +} + +static unsigned long dmatest_random(void) +{ + unsigned long buf; + + get_random_bytes(&buf, sizeof(buf)); + return buf; +} + +static unsigned int dmatest_verify(u8 *buf, unsigned int start, + unsigned int end, u8 expected) +{ + unsigned int i; + unsigned int error_count = 0; + + for (i = start; i < end; i++) { + if (buf[i] != expected) { + if (error_count < 32) + printk(KERN_ERR "dmatest: buf[0x%x] = %02x " + "(expected %02x)\n", + i, buf[i], expected); + error_count++; + } + } + + if (error_count > 32) + printk(KERN_ERR "dmatest: %u errors suppressed\n", + error_count - 32); + + return error_count; +} + +static int dmatest_func(void *data) +{ + struct dmatest *test = data; + struct dma_client *client = test->client; + struct dma_chan *chan; + int should_stop = 0; + unsigned int src_off, dst_off, len; + unsigned int error_count; + dma_cookie_t cookie; + enum dma_status status; + + for (;;) { + DEFINE_WAIT(chan_wait); + + for (;;) { + prepare_to_wait(&test->wq, &chan_wait, + TASK_UNINTERRUPTIBLE); + if (kthread_should_stop()) { + should_stop = 1; + break; + } + + rcu_read_lock(); + if (client->chan_count > 0) { + chan = list_entry(client->channels.next, + struct dma_chan, + client_node); + dma_chan_get(chan); + rcu_read_unlock(); + break; + } + rcu_read_unlock(); + schedule(); + } + finish_wait(&test->wq, &chan_wait); + + if (should_stop) + break; + + len = dmatest_random() % TEST_BUF_SIZE; + src_off = dmatest_random() % (TEST_BUF_SIZE - len); + dst_off = dmatest_random() % (TEST_BUF_SIZE - len); + + memset(test->srcbuf, SRC_PATTERN_OUTSIDE, src_off); + memset(test->srcbuf + src_off, SRC_PATTERN, len); + memset(test->srcbuf + src_off + len, + SRC_PATTERN_OUTSIDE, TEST_BUF_SIZE - (src_off + len)); + memset(test->dstbuf, POISON_OUTSIDE, dst_off); + memset(test->dstbuf + dst_off, POISON_UNINIT, len); + memset(test->dstbuf + dst_off + len, + POISON_OUTSIDE, TEST_BUF_SIZE - (dst_off + len)); + + cookie = dma_async_memcpy_buf_to_buf(chan, + test->dstbuf + dst_off, + test->srcbuf + src_off, + len); + if (dma_submit_error(cookie)) { + printk("dmatest: submit error: %d\n", cookie); + dma_chan_put(chan); + msleep(100); + continue; + } + dma_async_memcpy_issue_pending(chan); + + do { + msleep(1); + status = dma_async_memcpy_complete(chan, cookie, + NULL, NULL); + } while (status == DMA_IN_PROGRESS); + + dma_chan_put(chan); + + if (status == DMA_ERROR) { + printk("dmatest: error during copy\n"); + continue; + } + + error_count = 0; + + printk(KERN_INFO "dmatest: verifying source buffer...\n"); + error_count += dmatest_verify(test->srcbuf, 0, src_off, + SRC_PATTERN_OUTSIDE); + error_count += dmatest_verify(test->srcbuf, src_off, + src_off + len, + SRC_PATTERN); + error_count += dmatest_verify(test->srcbuf, src_off + len, + TEST_BUF_SIZE, + SRC_PATTERN_OUTSIDE); + + printk(KERN_INFO "dmatest: verifying dest buffer...\n"); + error_count += dmatest_verify(test->dstbuf, 0, dst_off, + POISON_OUTSIDE); + error_count += dmatest_verify(test->dstbuf, dst_off, + dst_off + len, SRC_PATTERN); + error_count += dmatest_verify(test->dstbuf, dst_off + len, + TEST_BUF_SIZE, POISON_OUTSIDE); + + if (error_count) + printk(KERN_ERR "dmatest: %u errors with " + "src_off=0x%x dst_off=0x%x len=0x%x\n", + error_count, src_off, dst_off, len); + else + printk(KERN_INFO "dmatest: No errors with " + "src_off=0x%x dst_off=0x%x len=0x%x\n", + src_off, dst_off, len); + } + + return 0; +} + +static int __init dmatest_init(void) +{ + struct dmatest *test = &dmatest_data; + int ret = -ENOMEM; + + test->srcbuf = kmalloc(TEST_BUF_SIZE, GFP_KERNEL); + if (!test->srcbuf) + goto err_srcbuf; + test->dstbuf = kmalloc(TEST_BUF_SIZE, GFP_KERNEL); + if (!test->dstbuf) + goto err_dstbuf; + + test->client = dma_async_client_register(dmatest_event); + if (!test->client) + goto err_client; + + dma_async_client_chan_request(test->client, 1); + + test->thread = kthread_run(dmatest_func, test, "kdmatestd"); + if (IS_ERR(test->thread)) { + ret = PTR_ERR(test->thread); + goto err_kthread; + } + + return 0; + +err_kthread: + dma_async_client_unregister(test->client); +err_client: + kfree(test->dstbuf); +err_dstbuf: + kfree(test->srcbuf); +err_srcbuf: + return ret; +} +module_init(dmatest_init); + +static void __exit dmatest_exit(void) +{ + int ret; + + ret = kthread_stop(dmatest_data.thread); + printk("dmatest: Thread exited with status %d\n", ret); + dma_async_client_unregister(dmatest_data.client); + kfree(dmatest_data.srcbuf); + kfree(dmatest_data.dstbuf); +} +module_exit(dmatest_exit); + +MODULE_AUTHOR("Haavard Skinnemoen "); +MODULE_LICENSE("GPL v2");