>-----Original Message-----
>From: Haavard Skinnemoen [mailto:[EMAIL PROTECTED] 
>
>This client tests DMA memcpy using various lengths and various offsets
>into the source and destination buffers. It will initialize both
>buffers with a know pattern and verify that the DMA engine copies the
>requested region and nothing more.
>
>Signed-off-by: Haavard Skinnemoen <[EMAIL PROTECTED]>
>---

[...]

>diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c
>new file mode 100644
>index 0000000..d9e9866
>--- /dev/null
>+++ b/drivers/dma/dmatest.c
>@@ -0,0 +1,272 @@
>+/*
>+ * 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 <linux/delay.h>
>+#include <linux/dmaengine.h>
>+#include <linux/init.h>
>+#include <linux/kthread.h>
>+#include <linux/module.h>
>+#include <linux/random.h>
>+#include <linux/wait.h>
>+
>+#define TEST_BUF_SIZE         (16384)

You might make this a module parameter so we can test with various
sizes.


>+
>+#define SRC_PATTERN           0x7c
>+#define SRC_PATTERN_OUTSIDE   0X8d
>+#define POISON_UNINIT         0x49
>+#define POISON_OUTSIDE                0x37
>+
>+struct dmatest {
>+      spinlock_t              lock;
>+      struct dma_client       client;
>+      struct task_struct      *thread;
>+      struct dma_chan         *chan;
>+      wait_queue_head_t       wq;
>+      u8                      *srcbuf;
>+      u8                      *dstbuf;
>+};
>+
>+static inline struct dmatest *to_dmatest(struct dma_client *client)
>+{
>+      return container_of(client, struct dmatest, client);
>+}
>+
>+static enum dma_state_client
>+dmatest_event(struct dma_client *client, struct dma_chan *chan,
>+              enum dma_state state)
>+{
>+      struct dmatest          *test = to_dmatest(client);
>+      enum dma_state_client   ack = DMA_DUP;

DMA_NAK is better default for DMA_RESOURCE_AVAILABLE once you've got the
channel you want, as that will stop DMAEngine from handing you more, and
doesn't bother the DMA_RESOURCE_REMOVED process.

>+
>+      spin_lock(&test->lock);
>+      switch (state) {
>+      case DMA_RESOURCE_AVAILABLE:
>+              if (!test->chan) {
>+                      printk(KERN_INFO "dmatest: Got channel %s\n",
>+                              chan->dev.bus_id);
>+                      test->chan = chan;
>+                      wake_up_interruptible(&test->wq);
>+                      ack = DMA_ACK;
>+              }
>+              break;

Do you ever want to test a specific channel?  Is there a way to identify
specific channels, perhaps by checking the PCI bus/chan/func?  This
could be useful in debugging specific hardware issues - a frilly extra
feature and very HW specific, but potentially useful.  I suppose this
might also depend upon your hardware - is there more than one channel in
your gizmo?

>+
>+      case DMA_RESOURCE_REMOVED:
>+              if (test->chan == chan) {

Should you use your test->lock here? 

>+                      printk(KERN_INFO "dmatest: Lost channel %s\n",
>+                              chan->dev.bus_id);
>+                      test->chan = NULL;
>+                      ack = DMA_ACK;
>+              }
>+              break;
>+
>+      default:

Since this is a test program, perhaps a printk() here might be useful...

>+              break;
>+      }
>+      spin_unlock(&test->lock);
>+
>+      return ack;
>+}
>+
>+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_chan *chan;
>+      bool            should_stop = false;
>+      unsigned int    src_off, dst_off, len;
>+      unsigned int    error_count;
>+      dma_cookie_t    cookie;
>+      enum dma_status status;
>+
>+      dma_cap_set(DMA_MEMCPY, test->client.cap_mask);
>+      dma_async_client_register(&test->client);
>+      dma_async_client_chan_request(&test->client);
>+
>+      for (;;) {
>+              DEFINE_WAIT(chan_wait);
>+
>+              pr_debug("dmatest: Waiting for a channel...\n");
>+              for (;;) {
>+                      spin_lock(&test->lock);
>+                      prepare_to_wait(&test->wq, &chan_wait,
>+                                      TASK_UNINTERRUPTIBLE);

Hmmm - so the only way to trigger the loop is to remove then add a
channel, basically rmmod and insmod your dma driver?  It would be nice
to have a method to trigger more than one test, maybe even a stream of
copy requests.  Or am I asking for too many features?  :-)

>+                      if (kthread_should_stop()) {
>+                              should_stop = true;
>+                              break;
>+                      }
>+
>+                      if (test->chan) {
>+                              chan = test->chan;
>+                              dma_chan_get(chan);

I suppose you are doing this extra get and put to be absolutely sure the
channel doesn't disappear out from under you, even though your
dmatest_event() already ACK'd the removal, right?

[...]


Thanks for posting this.

sln
--
======================================================================
Mr. Shannon Nelson                 LAN Access Division, Intel Corp.
[EMAIL PROTECTED]                I don't speak for Intel
(503) 712-7659                    Parents can't afford to be squeamish.
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to