diff mbox

[V2,2/5] spi: core: add spi_replace_transfers method

Message ID 1449501708-2228-3-git-send-email-kernel@martin.sperl.org (mailing list archive)
State New, archived
Headers show

Commit Message

Martin Sperl Dec. 7, 2015, 3:21 p.m. UTC
From: Martin Sperl <kernel@martin.sperl.org>

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 <kernel@martin.sperl.org>
---
 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

Comments

Andy Shevchenko Dec. 11, 2015, 2:37 p.m. UTC | #1
On Mon, Dec 7, 2015 at 5:21 PM,  <kernel@martin.sperl.org> wrote:
> From: Martin Sperl <kernel@martin.sperl.org>
>
> Add the spi_replace_transfers method that can get used
> to replace some spi_transfers from a spi_message with other
> transfers.

Few more implementation comments.

>
> Signed-off-by: Martin Sperl <kernel@martin.sperl.org>
> ---
>  drivers/spi/spi.c       |  130 +++++++++++++++++++++++++++++++++++++++++++++++
>  include/linux/spi/spi.h |   44 ++++++++++++++++
>  2 files changed, 174 insertions(+)
>
> 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;

srt sounds magic
rxfer might be better?

> +       int i;

unsigned int i; or size_t to be in align with inserted field.

> +
> +       /* 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;

Ditto.

> +
> +       /* 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;

ERR_PTR for this API seems better to have.

> +
> +       /* 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--) {

This is classical pattern for

while (--insert >= 0) {
}

> +               /* 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) {

…and
if (insert) {
}


> +                       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.
> --
> 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
Martin Sperl Dec. 11, 2015, 4:04 p.m. UTC | #2
> On 11.12.2015, at 15:37, Andy Shevchenko <andy.shevchenko@gmail.com> wrote:
> 
> On Mon, Dec 7, 2015 at 5:21 PM,  <kernel@martin.sperl.org> wrote:
>> +/* 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;
> 
> srt sounds magic
> rxfer might be better?
srt = Spi_Replaced_Transfer - if you like rxfer better I can replace it.

> 
>> +       int i;
> 
> unsigned int i; or size_t to be in align with inserted field.
will do.

>> +       int i;
> 
> Ditto.
> 
>> +
>> +       /* 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;
> 
> ERR_PTR for this API seems better to have.
Can do that.

>> +       srt->inserted = insert;
>> +       for (i = insert - 1; i >= 0 ; i--) {
> 
> This is classical pattern for
> 
> while (--insert >= 0) {
> }
did it slightly differently as size_t is always >= 0 - 
the main reason for using int here...

Martin


--
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 mbox

Patch

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.