summaryrefslogtreecommitdiff
path: root/cleopatre/devkit/plcdrv/src/debug_dump.c
diff options
context:
space:
mode:
Diffstat (limited to 'cleopatre/devkit/plcdrv/src/debug_dump.c')
-rw-r--r--cleopatre/devkit/plcdrv/src/debug_dump.c197
1 files changed, 197 insertions, 0 deletions
diff --git a/cleopatre/devkit/plcdrv/src/debug_dump.c b/cleopatre/devkit/plcdrv/src/debug_dump.c
new file mode 100644
index 0000000000..8e9f42e313
--- /dev/null
+++ b/cleopatre/devkit/plcdrv/src/debug_dump.c
@@ -0,0 +1,197 @@
+/* Cleopatre project {{{
+ *
+ * Copyright (C) 2012 Spidcom
+ *
+ * <<<Licence>>>
+ *
+ * }}} */
+/**
+ * \file src/debug_dump.c
+ * \brief Debug dump.
+ * \ingroup plcdrv
+ */
+#include <linux/kernel.h>
+#include <linux/compiler.h>
+#include <linux/bug.h>
+#include <linux/errno.h>
+#include <linux/gfp.h>
+
+#include "frame.h"
+#include "plcdrv.h"
+
+#include "debug_dump.h"
+
+/**
+ * Allocate a debug dump buffer for firmware.
+ * \param debug_dump_buffer the debug dump buffer to allocate
+ * \param debug_dump_buffer_length the length of the debug dump buffer
+ * \return error code
+ */
+static inline int
+debug_dump_buffer_alloc (void **debug_dump_buffer,
+ int debug_dump_buffer_length)
+{
+ /* Check parameters. */
+ BUG_ON (!debug_dump_buffer || *debug_dump_buffer);
+
+ /* Allocate debug dump buffer. */
+ *debug_dump_buffer = kmalloc (debug_dump_buffer_length,
+ GFP_ATOMIC | GFP_DMA);
+
+ /* If allocation failed. */
+ if (!(*debug_dump_buffer))
+ return -ENOMEM;
+
+ return 0;
+}
+
+/**
+ * Free a debug dump buffer.
+ * \param debug_dump_buffer the debug dump buffer to free
+ */
+static inline void
+debug_dump_buffer_free (void *debug_dump_buffer)
+{
+ /* Check parameters. */
+ BUG_ON (!debug_dump_buffer);
+
+ /* Free. */
+ kfree (debug_dump_buffer);
+}
+
+int
+debug_dump_read (plcdrv_t *priv, char __user *buf, size_t count,
+ loff_t *f_pos)
+{
+ static void *debug_dump_buffer = NULL;
+ static uint debug_dump_buffer_remaining_length = 0;
+ uint copied_data;
+ int error;
+
+ /* Check used parameter. */
+ BUG_ON (!f_pos);
+ BUG_ON (!priv);
+ /* Sanity check. */
+ BUG_ON (!priv->debug_dump.opened);
+
+ /* Allocate debug dump buffer if needed. */
+ if (!debug_dump_buffer)
+ {
+ /* Allocate. */
+ error = debug_dump_buffer_alloc (&debug_dump_buffer,
+ DEBUG_DUMP_BUFFER_LENGTH);
+ if (error != 0)
+ return error;
+ }
+
+ /* If we have nothing to copy to user. */
+ if (debug_dump_buffer_remaining_length == 0)
+ {
+ /* We need to handle the case where the buffer has been received while
+ * this code is not executed anymore (ctrl+c for example). There can
+ * be three cases:
+ * - we need to send a new buffer to Cesar because we do not have
+ * one to deal with,
+ * - the buffer has already been sent (debug_dump_waiting_for_buffer
+ * is set to true) but still not received
+ * (debug_dump_buffer_length_received set to -1): we need to wait.
+ * - the buffer has been received (debug_dump_buffer_length_received
+ * is not -1) but not processed by this code
+ * (debug_dump_waiting_for_buffer is set to false).
+ *
+ * Give debug dump buffer to firmware if this not already done. */
+ if (priv->debug_dump.waiting_for_buffer == false)
+ {
+ /* Debug dump buffer has been given to firmware. */
+ priv->debug_dump.buffer_length_received = -1;
+ priv->debug_dump.waiting_for_buffer = true;
+ frame_tx_mbx_debug_dump (priv, debug_dump_buffer,
+ DEBUG_DUMP_BUFFER_LENGTH);
+ }
+
+ /* Go to sleep until debug dump buffer has returned. */
+ error = wait_event_interruptible
+ (priv->debug_dump.wait_queue,
+ priv->debug_dump.buffer_length_received != -1);
+ if (error != 0)
+ return error;
+
+ /* Debug dump buffer received from firmware. */
+ priv->debug_dump.waiting_for_buffer = false;
+ debug_dump_buffer_remaining_length
+ = priv->debug_dump.buffer_length_received;
+ }
+
+ /* If this is not the last debug dump buffer. */
+ if (debug_dump_buffer_remaining_length)
+ {
+ BUG_ON ((int) debug_dump_buffer_remaining_length
+ > priv->debug_dump.buffer_length_received);
+ /* How much can we copy? */
+ copied_data = min (count, debug_dump_buffer_remaining_length);
+ /* Copy debug dump buffer to buffer for reading. */
+ if (copy_to_user (buf, debug_dump_buffer
+ + (priv->debug_dump.buffer_length_received
+ - debug_dump_buffer_remaining_length),
+ copied_data))
+ {
+ return -EFAULT;
+ }
+ debug_dump_buffer_remaining_length -= copied_data;
+
+ }
+ else
+ {
+ /* Finish, let's clean. */
+ debug_dump_buffer_free (debug_dump_buffer);
+ debug_dump_buffer = NULL;
+ debug_dump_buffer_remaining_length = 0;
+
+ copied_data = 0;
+ }
+
+ /* Update what have been done. */
+ *f_pos += copied_data;
+ return copied_data;
+}
+
+int
+debug_dump_open (plcdrv_t *priv)
+{
+ /* Check parameter. */
+ BUG_ON (!priv);
+
+ /* Open only one time. */
+ if (!priv->debug_dump.opened)
+ {
+ priv->debug_dump.opened = true;
+ return 0;
+ }
+ else
+ return -EBUSY;
+}
+
+void
+debug_dump_release (plcdrv_t *priv)
+{
+ /* Check parameter. */
+ BUG_ON (!priv);
+
+ BUG_ON (priv->debug_dump.opened == false);
+
+ /* Device now closed. */
+ priv->debug_dump.opened = false;
+}
+
+void
+debug_dump_init (plcdrv_t *priv)
+{
+ /* Check parameter. */
+ BUG_ON (!priv);
+
+ /* Initialise context. */
+ priv->debug_dump.buffer_length_received = -1;
+ priv->debug_dump.waiting_for_buffer = false;
+ init_waitqueue_head (&priv->debug_dump.wait_queue);
+ priv->debug_dump.opened = false;
+}