diff options
author | Martin Sperl <kernel@martin.sperl.org> | 2015-12-14 10:20:19 -0500 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2016-02-09 14:32:07 -0500 |
commit | 523baf5a0609690cb742b3662b7ccac0ea0b2ef2 (patch) | |
tree | e1b05ea7c4287cf1da5a72d4d0e446e3946165da | |
parent | d780c3711d9df9bacd56b71cf23443b895a331ca (diff) |
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>
-rw-r--r-- | drivers/spi/spi.c | 132 | ||||
-rw-r--r-- | include/linux/spi/spi.h | 45 |
2 files changed, 177 insertions, 0 deletions
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 894ed0357dd7..2ec8e66a8098 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c | |||
@@ -2107,6 +2107,138 @@ EXPORT_SYMBOL_GPL(spi_res_release); | |||
2107 | 2107 | ||
2108 | /*-------------------------------------------------------------------------*/ | 2108 | /*-------------------------------------------------------------------------*/ |
2109 | 2109 | ||
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 | |||
2110 | /* Core methods for SPI master protocol drivers. Some of the | 2242 | /* Core methods for SPI master protocol drivers. Some of the |
2111 | * other core methods are currently defined as inline functions. | 2243 | * other core methods are currently defined as inline functions. |
2112 | */ | 2244 | */ |
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 38204b584dc5..d71385756fee 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h | |||
@@ -890,6 +890,51 @@ spi_max_transfer_size(struct spi_device *spi) | |||
890 | 890 | ||
891 | /*---------------------------------------------------------------------------*/ | 891 | /*---------------------------------------------------------------------------*/ |
892 | 892 | ||
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 | |||
893 | /* All these synchronous SPI transfer routines are utilities layered | 938 | /* All these synchronous SPI transfer routines are utilities layered |
894 | * over the core async transfer primitive. Here, "synchronous" means | 939 | * over the core async transfer primitive. Here, "synchronous" means |
895 | * they will sleep uninterruptibly until the async transfer completes. | 940 | * they will sleep uninterruptibly until the async transfer completes. |