Skip to content

Commit 523baf5

Browse files
msperlbroonie
authored andcommitted
spi: core: add spi_replace_transfers method
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> Signed-off-by: Mark Brown <broonie@kernel.org>
1 parent d780c37 commit 523baf5

File tree

2 files changed

+177
-0
lines changed

2 files changed

+177
-0
lines changed

drivers/spi/spi.c

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2107,6 +2107,138 @@ EXPORT_SYMBOL_GPL(spi_res_release);
21072107

21082108
/*-------------------------------------------------------------------------*/
21092109

2110+
/* Core methods for spi_message alterations */
2111+
2112+
static void __spi_replace_transfers_release(struct spi_master *master,
2113+
struct spi_message *msg,
2114+
void *res)
2115+
{
2116+
struct spi_replaced_transfers *rxfer = res;
2117+
size_t i;
2118+
2119+
/* call extra callback if requested */
2120+
if (rxfer->release)
2121+
rxfer->release(master, msg, res);
2122+
2123+
/* insert replaced transfers back into the message */
2124+
list_splice(&rxfer->replaced_transfers, rxfer->replaced_after);
2125+
2126+
/* remove the formerly inserted entries */
2127+
for (i = 0; i < rxfer->inserted; i++)
2128+
list_del(&rxfer->inserted_transfers[i].transfer_list);
2129+
}
2130+
2131+
/**
2132+
* spi_replace_transfers - replace transfers with several transfers
2133+
* and register change with spi_message.resources
2134+
* @msg: the spi_message we work upon
2135+
* @xfer_first: the first spi_transfer we want to replace
2136+
* @remove: number of transfers to remove
2137+
* @insert: the number of transfers we want to insert instead
2138+
* @release: extra release code necessary in some circumstances
2139+
* @extradatasize: extra data to allocate (with alignment guarantees
2140+
* of struct @spi_transfer)
2141+
*
2142+
* Returns: pointer to @spi_replaced_transfers,
2143+
* PTR_ERR(...) in case of errors.
2144+
*/
2145+
struct spi_replaced_transfers *spi_replace_transfers(
2146+
struct spi_message *msg,
2147+
struct spi_transfer *xfer_first,
2148+
size_t remove,
2149+
size_t insert,
2150+
spi_replaced_release_t release,
2151+
size_t extradatasize,
2152+
gfp_t gfp)
2153+
{
2154+
struct spi_replaced_transfers *rxfer;
2155+
struct spi_transfer *xfer;
2156+
size_t i;
2157+
2158+
/* allocate the structure using spi_res */
2159+
rxfer = spi_res_alloc(msg->spi, __spi_replace_transfers_release,
2160+
insert * sizeof(struct spi_transfer)
2161+
+ sizeof(struct spi_replaced_transfers)
2162+
+ extradatasize,
2163+
gfp);
2164+
if (!rxfer)
2165+
return ERR_PTR(-ENOMEM);
2166+
2167+
/* the release code to invoke before running the generic release */
2168+
rxfer->release = release;
2169+
2170+
/* assign extradata */
2171+
if (extradatasize)
2172+
rxfer->extradata =
2173+
&rxfer->inserted_transfers[insert];
2174+
2175+
/* init the replaced_transfers list */
2176+
INIT_LIST_HEAD(&rxfer->replaced_transfers);
2177+
2178+
/* assign the list_entry after which we should reinsert
2179+
* the @replaced_transfers - it may be spi_message.messages!
2180+
*/
2181+
rxfer->replaced_after = xfer_first->transfer_list.prev;
2182+
2183+
/* remove the requested number of transfers */
2184+
for (i = 0; i < remove; i++) {
2185+
/* if the entry after replaced_after it is msg->transfers
2186+
* then we have been requested to remove more transfers
2187+
* than are in the list
2188+
*/
2189+
if (rxfer->replaced_after->next == &msg->transfers) {
2190+
dev_err(&msg->spi->dev,
2191+
"requested to remove more spi_transfers than are available\n");
2192+
/* insert replaced transfers back into the message */
2193+
list_splice(&rxfer->replaced_transfers,
2194+
rxfer->replaced_after);
2195+
2196+
/* free the spi_replace_transfer structure */
2197+
spi_res_free(rxfer);
2198+
2199+
/* and return with an error */
2200+
return ERR_PTR(-EINVAL);
2201+
}
2202+
2203+
/* remove the entry after replaced_after from list of
2204+
* transfers and add it to list of replaced_transfers
2205+
*/
2206+
list_move_tail(rxfer->replaced_after->next,
2207+
&rxfer->replaced_transfers);
2208+
}
2209+
2210+
/* create copy of the given xfer with identical settings
2211+
* based on the first transfer to get removed
2212+
*/
2213+
for (i = 0; i < insert; i++) {
2214+
/* we need to run in reverse order */
2215+
xfer = &rxfer->inserted_transfers[insert - 1 - i];
2216+
2217+
/* copy all spi_transfer data */
2218+
memcpy(xfer, xfer_first, sizeof(*xfer));
2219+
2220+
/* add to list */
2221+
list_add(&xfer->transfer_list, rxfer->replaced_after);
2222+
2223+
/* clear cs_change and delay_usecs for all but the last */
2224+
if (i) {
2225+
xfer->cs_change = false;
2226+
xfer->delay_usecs = 0;
2227+
}
2228+
}
2229+
2230+
/* set up inserted */
2231+
rxfer->inserted = insert;
2232+
2233+
/* and register it with spi_res/spi_message */
2234+
spi_res_add(msg, rxfer);
2235+
2236+
return rxfer;
2237+
}
2238+
EXPORT_SYMBOL_GPL(spi_replace_transfers);
2239+
2240+
/*-------------------------------------------------------------------------*/
2241+
21102242
/* Core methods for SPI master protocol drivers. Some of the
21112243
* other core methods are currently defined as inline functions.
21122244
*/

include/linux/spi/spi.h

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -890,6 +890,51 @@ spi_max_transfer_size(struct spi_device *spi)
890890

891891
/*---------------------------------------------------------------------------*/
892892

893+
/* SPI transfer replacement methods which make use of spi_res */
894+
895+
/**
896+
* struct spi_replaced_transfers - structure describing the spi_transfer
897+
* replacements that have occurred
898+
* so that they can get reverted
899+
* @release: some extra release code to get executed prior to
900+
* relasing this structure
901+
* @extradata: pointer to some extra data if requested or NULL
902+
* @replaced_transfers: transfers that have been replaced and which need
903+
* to get restored
904+
* @replaced_after: the transfer after which the @replaced_transfers
905+
* are to get re-inserted
906+
* @inserted: number of transfers inserted
907+
* @inserted_transfers: array of spi_transfers of array-size @inserted,
908+
* that have been replacing replaced_transfers
909+
*
910+
* note: that @extradata will point to @inserted_transfers[@inserted]
911+
* if some extra allocation is requested, so alignment will be the same
912+
* as for spi_transfers
913+
*/
914+
struct spi_replaced_transfers;
915+
typedef void (*spi_replaced_release_t)(struct spi_master *master,
916+
struct spi_message *msg,
917+
struct spi_replaced_transfers *res);
918+
struct spi_replaced_transfers {
919+
spi_replaced_release_t release;
920+
void *extradata;
921+
struct list_head replaced_transfers;
922+
struct list_head *replaced_after;
923+
size_t inserted;
924+
struct spi_transfer inserted_transfers[];
925+
};
926+
927+
extern struct spi_replaced_transfers *spi_replace_transfers(
928+
struct spi_message *msg,
929+
struct spi_transfer *xfer_first,
930+
size_t remove,
931+
size_t insert,
932+
spi_replaced_release_t release,
933+
size_t extradatasize,
934+
gfp_t gfp);
935+
936+
/*---------------------------------------------------------------------------*/
937+
893938
/* All these synchronous SPI transfer routines are utilities layered
894939
* over the core async transfer primitive. Here, "synchronous" means
895940
* they will sleep uninterruptibly until the async transfer completes.

0 commit comments

Comments
 (0)