From patchwork Mon Dec 7 15:21:45 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Sperl X-Patchwork-Id: 7786491 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 DACB09F1C2 for ; Mon, 7 Dec 2015 15:22:13 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 6567F204CF for ; Mon, 7 Dec 2015 15:22:11 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 227B2204B5 for ; Mon, 7 Dec 2015 15:22:10 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S933186AbbLGPWH (ORCPT ); Mon, 7 Dec 2015 10:22:07 -0500 Received: from 212-186-180-163.dynamic.surfer.at ([212.186.180.163]:38791 "EHLO cgate.sperl.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S933158AbbLGPWE (ORCPT ); Mon, 7 Dec 2015 10:22:04 -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 6360744; Mon, 07 Dec 2015 15:21:57 +0000 From: kernel@martin.sperl.org To: Mark Brown , Stephen Warren , Lee Jones , Eric Anholt , linux-spi@vger.kernel.org, linux-rpi-kernel@lists.infradead.org Cc: Martin Sperl Subject: [PATCH V2 2/5] spi: core: add spi_replace_transfers method Date: Mon, 7 Dec 2015 15:21:45 +0000 Message-Id: <1449501708-2228-3-git-send-email-kernel@martin.sperl.org> X-Mailer: git-send-email 1.7.10.4 In-Reply-To: <1449501708-2228-1-git-send-email-kernel@martin.sperl.org> References: <1449501708-2228-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, T_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 Add the spi_replace_transfers method that can get used to replace some spi_transfers from a spi_message with other transfers. Signed-off-by: Martin Sperl --- drivers/spi/spi.c | 130 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/spi/spi.h | 44 ++++++++++++++++ 2 files changed, 174 insertions(+) -- 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 fb39d23..97c77b3 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -2087,6 +2087,136 @@ EXPORT_SYMBOL_GPL(spi_res_release); /*-------------------------------------------------------------------------*/ +/* Core methods for spi_message alterations */ + +static void __spi_replace_transfers_release(struct spi_master *master, + struct spi_message *msg, + void *res) +{ + struct spi_replaced_transfers *srt = res; + int i; + + /* call extra callback if requested */ + if (srt->release) + srt->release(master, msg, res); + + /* insert replaced transfers back into the message */ + list_splice(&srt->replaced_transfers, srt->replaced_after); + + /* remove the formerly inserted entries */ + for (i = 0; i < srt->inserted; i++) + list_del(&srt->inserted_transfers[i].transfer_list); +} + +/** + * spi_replace_transfers - replace transfers with several transfers + * and register change with spi_message.resources + * @msg: the spi_message we work upon + * @xfer_first: the first spi_transfer we want to replace + * @remove: number of transfers to remove + * @insert: the number of transfers we want to insert instead + * @release: extra release code necessary in some circumstances + * @extradatasize: extra data to allocate (with alignment guarantees + * of struct @spi_transfer) + * + * Returns: pointer to @spi_replaced_transfers, + * NULL in case of errors + */ +struct spi_replaced_transfers *spi_replace_transfers( + struct spi_message *msg, + struct spi_transfer *xfer_first, + size_t remove, + size_t insert, + spi_replaced_release_t release, + size_t extradatasize) +{ + struct spi_replaced_transfers *srt; + int i; + + /* allocate the structure using spi_res */ + srt = spi_res_alloc(msg->spi, __spi_replace_transfers_release, + insert * sizeof(struct spi_transfer) + + sizeof(struct spi_replaced_transfers) + + extradatasize, + 0); + if (!srt) + return NULL; + + /* the release code to invoke before running the generic release */ + srt->release = release; + + /* assign extradata */ + if (extradatasize) + srt->extradata = &srt->inserted_transfers[srt->inserted]; + + /* init the replaced_transfers list */ + INIT_LIST_HEAD(&srt->replaced_transfers); + + /* assign the list_entry after which we should reinsert + * the @replaced_transfers - it may be spi_message.messages! + */ + srt->replaced_after = xfer_first->transfer_list.prev; + + /* remove the requested number of transfers */ + for (i = 0; i < remove; i++) { + /* if the entry after replaced_after it is msg->transfers + * then we have been requested to remove more transfers + * than are in the list + */ + if (srt->replaced_after->next == &msg->transfers) { + dev_err(&msg->spi->dev, + "requested to remove more spi_transfers than are available\n"); + /* insert replaced transfers back into the message */ + list_splice(&srt->replaced_transfers, + srt->replaced_after); + + /* free the spi_replace_transfer structure */ + spi_res_free(srt); + + /* and return with an error */ + return NULL; + } + + /* remove the entry after replaced_after from list of + * transfers and add it to list of replaced_transfers + */ + list_move_tail(srt->replaced_after->next, + &srt->replaced_transfers); + } + + /* create copy of the given xfer with identical settings + * based on the first transfer to get removed + * note that we run in reverse to keep the array order + * match the order in transfer_list + */ + srt->inserted = insert; + for (i = insert - 1; i >= 0 ; i--) { + /* copy all spi_transfer data */ + memcpy(&srt->inserted_transfers[i], xfer_first, + sizeof(*xfer_first)); + + /* for all but the last inserted_transfer, + * clear some settings + */ + if (i < insert - 1) { + srt->inserted_transfers[i].cs_change = 0; + srt->inserted_transfers[i].delay_usecs = 0; + } + + /* add after srt->replaced_after */ + list_add(&srt->inserted_transfers[i].transfer_list, + srt->replaced_after); + } + + /* and register it with spi_res/spi_message */ + spi_res_add(msg, srt); + + return srt; +} +EXPORT_SYMBOL_GPL(spi_replace_transfers); + +/*-------------------------------------------------------------------------*/ + /* Core methods for SPI master protocol drivers. Some of the * other core methods are currently defined as inline functions. */ diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 7e74e0e..35b5f17 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -875,6 +875,50 @@ extern int spi_async_locked(struct spi_device *spi, /*---------------------------------------------------------------------------*/ +/* SPI transfer replacement methods which make use of spi_res */ + +/** + * struct spi_replaced_transfers - structure describing the spi_transfer + * replacements that have occurred + * so that they can get reverted + * @release: some extra release code to get executed prior to + * relasing this structure + * @extradata: pointer to some extra data if requested or NULL + * @replaced_transfers: transfers that have been replaced and which need + * to get restored + * @replaced_after: the transfer after which the @replaced_transfers + * are to get re-inserted + * @inserted: number of transfers inserted + * @inserted_transfers: array of spi_transfers of array-size @inserted, + * that have been replacing replaced_transfers + * + * note: that @extradata will point to @inserted_transfers[@inserted] + * if some extra allocation is requested, so alignment will be the same + * as for spi_transfers + */ +struct spi_replaced_transfers; +typedef void (*spi_replaced_release_t)(struct spi_master *master, + struct spi_message *msg, + struct spi_replaced_transfers *res); +struct spi_replaced_transfers { + spi_replaced_release_t release; + void *extradata; + struct list_head replaced_transfers; + struct list_head *replaced_after; + size_t inserted; + struct spi_transfer inserted_transfers[]; +}; + +extern struct spi_replaced_transfers *spi_replace_transfers( + struct spi_message *msg, + struct spi_transfer *xfer_first, + size_t remove, + size_t insert, + spi_replaced_release_t release, + size_t extradatasize); + +/*---------------------------------------------------------------------------*/ + /* All these synchronous SPI transfer routines are utilities layered * over the core async transfer primitive. Here, "synchronous" means * they will sleep uninterruptibly until the async transfer completes.