From patchwork Mon Dec 21 11:52:46 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Sperl X-Patchwork-Id: 7895501 Return-Path: X-Original-To: patchwork-linux-spi@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 617AE9F1AF for ; Mon, 21 Dec 2015 11:53:16 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 4ACCF2044B for ; Mon, 21 Dec 2015 11:53:15 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 0C3B42047D for ; Mon, 21 Dec 2015 11:53:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751677AbbLULxG (ORCPT ); Mon, 21 Dec 2015 06:53:06 -0500 Received: from 212-186-180-163.dynamic.surfer.at ([212.186.180.163]:40071 "EHLO cgate.sperl.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751947AbbLULxD (ORCPT ); Mon, 21 Dec 2015 06:53:03 -0500 Received: from raspcm.intern.sperl.org (account martin@sperl.org [10.10.10.41] verified) by sperl.org (CommuniGate Pro SMTP 6.1.2) with ESMTPSA id 6390324; Mon, 21 Dec 2015 11:52:58 +0000 From: kernel@martin.sperl.org To: Mark Brown , linux-spi@vger.kernel.org Cc: Martin Sperl Subject: [PATCH v2 1/8] spi: core: add spi_message_dump and spi_transfer_dump Date: Mon, 21 Dec 2015 11:52:46 +0000 Message-Id: <1450698772-2379-4-git-send-email-kernel@martin.sperl.org> X-Mailer: git-send-email 1.7.10.4 In-Reply-To: <1450698772-2379-1-git-send-email-kernel@martin.sperl.org> References: <1450698772-2379-1-git-send-email-kernel@martin.sperl.org> Sender: linux-spi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-spi@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Martin Sperl 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 --- 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 -- 1.7.10.4 -- To unsubscribe from this list: send the line "unsubscribe linux-spi" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html 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 #include #include +#include #include #include #include @@ -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 */ + 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) +{ + 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) + 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); + } +} +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); +} /*---------------------------------------------------------------------------*/