Re: [PATCH v2 1/8] spi: core: add spi_message_dump and spi_transfer_dump

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

 



On Mon, Dec 21, 2015 at 1:52 PM,  <kernel@xxxxxxxxxxxxxxxx> wrote:
> 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       |  169 +++++++++++++++++++++++++++++++++++++++++++++++
>  include/linux/spi/spi.h |   24 +++++++
>  2 files changed, 193 insertions(+)
>
> Changelog:
> V1->V2: * changes recommended by Andy Shevchenko
>         * slight reorganisation of code - specifically remove
>           hex_dump_to_buffer and use %*ph instead
>         * removal of one helper function and renaming another
>         * change in method parameters to allow changing the amount
>           of lines to dump at the head independently from tail
>
> diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
> index 9964835..bf623db 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,174 @@ int spi_register_board_info(struct spi_board_info const *info, unsigned n)
>         return 0;
>  }
>
> +const unsigned spi_dump_bytes_per_line = 16;
> +static void __spi_transfer_dump_chunk(struct spi_device *spi, char *prefix,
> +                                     const void *ptr, size_t start,
> +                                     size_t len)
> +{
> +       const size_t spi_dump_bytes_per_line = 16;
> +       char hexdata[spi_dump_bytes_per_line * 3];
> +       size_t i, l;
> +
> +       for (i = start; i < len; i += spi_dump_bytes_per_line) {
> +               /* format the next 16 bytes */
> +               snprintf(hexdata, sizeof(hexdata), "%16ph", ptr + i);
> +
> +               /* truncate the unnecessarily printed bytes */

Again, you don't need the intermediate buffer along with this all
stuff. Just use %*ph properly (hint: star is put here and elsewhere on
purpose).

drivers/hid/i2c-hid/i2c-hid.c:187:      i2c_hid_dbg(ihid, "%s:
cmd=%*ph\n", __func__, length, cmd->data);

> +               l = min_t(size_t, len - i, spi_dump_bytes_per_line) * 3 - 1;
> +               hexdata[l] = 0;
> +
> +               /* and print data including address and offset */
> +               dev_info(&spi->dev, "%soff=0x%.5zx ptr=0x%pK: %s\n",
> +                        prefix, i, ptr + i, hexdata);
> +       }
> +}
> +
> +static void spi_transfer_dump_buffer(struct spi_device *spi, char *prefix,
> +                                    const void *ptr, size_t len,
> +                                    size_t head_len, size_t tail_len)
> +{

The function body looks too complicated. Can you describe what
function does in different cases?

> +       size_t tail_start;
> +
> +       /* dump head if requested */
> +       if (head_len) {
> +               /* calculate effective head_len - aligning if necessary */
> +               head_len = (len <= head_len) ?
> +                          len :
> +                          roundup(head_len, spi_dump_bytes_per_line);
> +
> +               /* dump the head */
> +               __spi_transfer_dump_chunk(spi, prefix, ptr, 0, head_len);
> +
> +               /* if we dumped everything return immediately */
> +               if (len == head_len)
> +                       return;
> +       }
> +
> +       /* return if no tail_len is requested */
> +       if (!tail_len)
> +               return;
> +
> +       /* calculate real tail start offset aligning it */
> +       tail_start = (tail_len >= len) ?
> +                    head_len :
> +                    rounddown(len - tail_len, spi_dump_bytes_per_line);
> +
> +       /* special handling needed if we have been dumping head */
> +       if (head_len) {
> +               if (tail_start > head_len)
> +                       /* we are not overlapping */
> +                       dev_info(&spi->dev,
> +                                "%struncated - continuing at offset %04x\n",
> +                                prefix, tail_start);
> +               else
> +                       /* we are overlapping, so continue at head_len */
> +                       tail_start = head_len;
> +       }
> +
> +       /* dump the tail */
> +       __spi_transfer_dump_chunk(spi, prefix, ptr, tail_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
> + * @head_len: bytes to dump from the start of the buffers
> + *            (rounded up to multiple of 16)
> + * @tail_len: bytes to dump from the end of the buffers
> + *            (rounded up so that tail dump starts 16 byte aligned)
> + */
> +void spi_transfer_dump(struct spi_device *spi,
> +                      struct spi_message *msg,
> +                      struct spi_transfer *xfer,
> +                      size_t head_len, size_t tail_len)
> +{
> +       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 (head_len | tail_len)

I think || looks a bit more logical here and below.

> +                       spi_transfer_dump_buffer(spi, "\t",
> +                                                xfer->tx_buf, xfer->len,
> +                                                head_len, tail_len);
> +       }
> +       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 (head_len | tail_len)

> +                       spi_transfer_dump_buffer(spi, "\t",
> +                                                xfer->rx_buf, xfer->len,
> +                                                head_len, tail_len);

Is the DMA buffer is in coherent memory? Otherwise you have to call
dma_sync for it before reading on CPU side.

> +       }
> +}
> +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
> + * @head_len: bytes to dump from the start of the buffers
> + *            (rounded up to multiple of 16)
> + * @tail_len: bytes to dump from the end of the buffers
> + *            (rounded up so that tail dump starts 16 byte aligned)
> + * @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 head_len, size_t tail_len,
> +                            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, head_len, tail_len);
> +               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..13f8f06 100644
> --- a/include/linux/spi/spi.h
> +++ b/include/linux/spi/spi.h
> @@ -897,6 +897,30 @@ 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_head, size_t dump_tail);
> +
> +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_head, size_t dump_tail,
> +                                   spi_transfer_dump_custom_t custom,
> +                                   void *context);
> +
> +static inline void spi_message_dump(struct spi_device *spi,
> +                                   struct spi_message *msg,
> +                                   size_t dump_head, size_t dump_tail)
> +{
> +       spi_message_dump_custom(spi, msg, dump_head, dump_tail, 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



-- 
With Best Regards,
Andy Shevchenko
--
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