[PATCH 1/4] spi: core: add spi_message_dump and spi_transfer_dump

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Martin Sperl <kernel@xxxxxxxxxxxxxxxx>

This implements a means to dump a spi_message or spi_transfer.

spi_loop_back_test requires a means to report on failed transfers
(including payload data), so it makes use of this.

Such a functionality can also be helpful during development of
other drivers, so it has been exposed as a general facility.

Signed-off-by: Martin Sperl <kernel@xxxxxxxxxxxxxxxx>
---
 drivers/spi/spi.c       |  146 +++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/spi/spi.h |   30 ++++++++++
 2 files changed, 176 insertions(+)

diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 9964835..6e9157f 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -31,6 +31,7 @@
 #include <linux/of_gpio.h>
 #include <linux/pm_runtime.h>
 #include <linux/pm_domain.h>
+#include <linux/printk.h>
 #include <linux/export.h>
 #include <linux/sched/rt.h>
 #include <linux/delay.h>
@@ -718,6 +719,151 @@ int spi_register_board_info(struct spi_board_info const *info, unsigned n)
 	return 0;
 }

+static void __spi_transfer_dump_chunk(struct spi_device *spi, char *pre,
+				      const void *ptr, size_t start,
+				      size_t len)
+{
+	unsigned char linebuf[32 * 3 + 2 + 32 + 1];
+	int i;
+
+	/* because we want to use dev_info as well as print
+	 * offset value as well as pointer
+	 * we can not use print_hex_dump directly
+	 */
+	for (i = start; i < len; i += 16) {
+		hex_dump_to_buffer(ptr + i, min_t(int, len - i, 16),
+				   16, 1,
+				   linebuf, sizeof(linebuf), 0);
+		dev_info(&spi->dev, "%soff=%.8x ptr=%p: %s\n",
+			 pre, i, ptr + i, linebuf);
+	}
+}
+
+static void spi_transfer_dump_buffer(struct spi_device *spi, char *pre,
+				     const void *ptr, size_t len,
+				     size_t dump_size)
+{
+	int start;
+
+	/* align dump_size on 32 bytes */
+	if (dump_size & 31)
+		dump_size += 32 - (dump_size & 31);
+
+	/* dump the whole chunk in one go if needed */
+	if (len <= dump_size) {
+		__spi_transfer_dump_chunk(spi, pre, ptr, 0, len);
+		return;
+	}
+
+	/* otherwise we need to dump chunks - head first */
+	__spi_transfer_dump_chunk(spi, pre, ptr, 0, dump_size / 2);
+
+	/* calculate where we need to continue */
+	start = len - dump_size / 2;
+	start &= ~15; /* align on the last multiple of 16 */
+
+	/* message about truncating */
+	dev_info(&spi->dev, "%s truncated - continuing at offset %04x\n",
+		 pre, start);
+
+	/* now the tail */
+	__spi_transfer_dump_chunk(spi, pre, ptr, start, len);
+}
+
+/**
+ * spi_transfer_dump - dump all the essential information
+ *                     of a @spi_transfer, when dump_size is set,
+ *                     then hex-dump that many bytes of data
+ * @spi:       @spi_device for which to dump this (dev_info)
+ * @msg:       @spi_message to which xfer belongs
+ * @xfer:      @spi_transfer to dump
+ * @dump_size: total number of bytes to dump of each buffer
+ *             (multiple of 32 if not rounded up)
+ */
+void spi_transfer_dump(struct spi_device *spi,
+		       struct spi_message *msg,
+		       struct spi_transfer *xfer,
+		       size_t dump_size)
+{
+	struct device *dev = &spi->dev;
+
+	dev_info(dev, "  spi_transfer@%pK\n", xfer);
+	dev_info(dev, "    speed_hz:    %u\n", xfer->speed_hz);
+	dev_info(dev, "    len:         %u\n", xfer->len);
+	dev_info(dev, "    tx_nbits:    %u\n", xfer->tx_nbits);
+	dev_info(dev, "    rx_nbits:    %u\n", xfer->rx_nbits);
+	dev_info(dev, "    bits/word:   %u\n", xfer->bits_per_word);
+	if (xfer->delay_usecs)
+		dev_info(dev, "    delay_usecs: %u\n",
+			 xfer->delay_usecs);
+	if (xfer->cs_change)
+		dev_info(dev, "    cs_change\n");
+	if (xfer->tx_buf) {
+		dev_info(dev, "    tx_buf:      %pK\n", xfer->tx_buf);
+		if (xfer->tx_dma)
+			dev_info(dev, "    tx_dma:      %pad\n",
+				 &xfer->tx_dma);
+		if (dump_size)
+			spi_transfer_dump_buffer(spi, "      ",
+						 xfer->tx_buf, xfer->len,
+						 dump_size);
+	}
+	if (xfer->rx_buf) {
+		dev_info(dev, "    rx_buf:      %pK\n", xfer->rx_buf);
+		if (xfer->rx_dma)
+			dev_info(dev, "    rx_dma:      %pad\n",
+				 &xfer->rx_dma);
+		if (dump_size)
+			spi_transfer_dump_buffer(spi, "      ",
+						 xfer->rx_buf, xfer->len,
+						 dump_size);
+	}
+}
+EXPORT_SYMBOL_GPL(spi_transfer_dump);
+
+/**
+ * spi_message_dump_custom - dump a spi message with ability to have
+ *                           a custom dump method per transfer
+ * @spi:       @spi_device for which to dump this (dev_info)
+ * @msg:       @spi_message to dump
+ * @dump_size: total number of bytes to dump of each buffer
+ * @custom:    custom dump code to execute per transfer
+ * @context:   context to pass to the custom dump code
+ *
+ * uses dev_info() to dump the lines
+ */
+void spi_message_dump_custom(struct spi_device *spi,
+			     struct spi_message *msg,
+			     size_t dump_size,
+			     spi_transfer_dump_custom_t custom,
+			     void *context)
+{
+	struct device *dev = &spi->dev;
+	struct spi_transfer *xfer;
+
+	/* dump the message */
+	dev_info(dev, "spi_msg@%pK\n", msg);
+	if (msg->status)
+		dev_info(dev, "  status:         %d\n", msg->status);
+	dev_info(dev, "  frame_length:   %zu\n", msg->frame_length);
+	dev_info(dev, "  actual_length:  %zu\n", msg->actual_length);
+	if (msg->complete)
+		dev_info(dev, "  complete:       %pF\n", msg->complete);
+	if (msg->context)
+		dev_info(dev, "  context:        %pF\n", msg->context);
+	if (msg->is_dma_mapped)
+		dev_info(dev, "  is_dma_mapped\n");
+	dev_info(dev, "  transfers-head: %pK\n", &msg->transfers);
+
+	/* dump transfers themselves */
+	list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+		spi_transfer_dump(spi, msg, xfer, dump_size);
+		if (custom)
+			custom(spi, msg, xfer, context);
+	}
+}
+EXPORT_SYMBOL_GPL(spi_message_dump_custom);
+
 /*-------------------------------------------------------------------------*/

 static void spi_set_cs(struct spi_device *spi, bool enable)
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index f055a47..a17be97 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -897,6 +897,36 @@ extern int spi_setup(struct spi_device *spi);
 extern int spi_async(struct spi_device *spi, struct spi_message *message);
 extern int spi_async_locked(struct spi_device *spi,
 			    struct spi_message *message);
+/*---------------------------------------------------------------------------*/
+
+extern void spi_transfer_dump(struct spi_device *spi,
+			      struct spi_message *msg,
+			      struct spi_transfer *xfer,
+			      size_t dump_size);
+
+typedef void (*spi_transfer_dump_custom_t)(struct spi_device *spi,
+					   struct spi_message *msg,
+					   struct spi_transfer *xfer,
+					   void *context);
+
+extern void spi_message_dump_custom(struct spi_device *spi,
+				    struct spi_message *msg,
+				    size_t dump_size,
+				    spi_transfer_dump_custom_t custom,
+				    void *context);
+
+static inline void spi_message_dump_data(struct spi_device *spi,
+					 struct spi_message *msg,
+					 size_t dump_size)
+{
+	spi_message_dump_custom(spi, msg, dump_size, NULL, NULL);
+}
+
+static inline void spi_message_dump(struct spi_device *spi,
+				    struct spi_message *msg)
+{
+	spi_message_dump_custom(spi, msg, 0, NULL, NULL);
+}

 /*---------------------------------------------------------------------------*/

--
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Kernel]     [Linux ARM (vger)]     [Linux ARM MSM]     [Linux Omap]     [Linux Arm]     [Linux Tegra]     [Fedora ARM]     [Linux for Samsung SOC]     [eCos]     [Linux Fastboot]     [Gcc Help]     [Git]     [DCCP]     [IETF Announce]     [Security]     [Linux MIPS]     [Yosemite Campsites]

  Powered by Linux