summaryrefslogtreecommitdiffstats
path: root/drivers/staging/greybus/kernel_ver.h
diff options
context:
space:
mode:
authorRui Miguel Silva <rui.silva@linaro.org>2015-06-24 23:20:27 +0100
committerGreg Kroah-Hartman <gregkh@google.com>2015-06-24 17:34:46 -0700
commit8a01b408a6b071862efadf905bad60d10612e78a (patch)
treefc004525e029b111289996d3013b963da35e3c7d /drivers/staging/greybus/kernel_ver.h
parent5656ab99a0ff9136613ab1da509ccd6d7652d173 (diff)
greybus: kernel_ver: add sg copy operations for kernel < 3.11
For older kernel, < 3.11, no copy to/from buffer with skip support was defined. This could break builds for this versions of kernel. Add them here. Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> Tested-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
Diffstat (limited to 'drivers/staging/greybus/kernel_ver.h')
-rw-r--r--drivers/staging/greybus/kernel_ver.h100
1 files changed, 100 insertions, 0 deletions
diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h
index f95f302b2b2d..4fb949ba9b51 100644
--- a/drivers/staging/greybus/kernel_ver.h
+++ b/drivers/staging/greybus/kernel_ver.h
@@ -135,4 +135,104 @@ static inline void sysfs_remove_groups(struct kobject *kobj,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)
#define MMC_POWER_UNDEFINED_SUPPORTED
#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0)
+#include <linux/scatterlist.h>
+static inline bool sg_miter_get_next_page(struct sg_mapping_iter *miter)
+{
+ if (!miter->__remaining) {
+ struct scatterlist *sg;
+ unsigned long pgoffset;
+
+ if (!__sg_page_iter_next(&miter->piter))
+ return false;
+
+ sg = miter->piter.sg;
+ pgoffset = miter->piter.sg_pgoffset;
+
+ miter->__offset = pgoffset ? 0 : sg->offset;
+ miter->__remaining = sg->offset + sg->length -
+ (pgoffset << PAGE_SHIFT) - miter->__offset;
+ miter->__remaining = min_t(unsigned long, miter->__remaining,
+ PAGE_SIZE - miter->__offset);
+ }
+
+ return true;
+}
+
+static inline bool sg_miter_skip(struct sg_mapping_iter *miter, off_t offset)
+{
+ sg_miter_stop(miter);
+
+ while (offset) {
+ off_t consumed;
+
+ if (!sg_miter_get_next_page(miter))
+ return false;
+
+ consumed = min_t(off_t, offset, miter->__remaining);
+ miter->__offset += consumed;
+ miter->__remaining -= consumed;
+ offset -= consumed;
+ }
+
+ return true;
+}
+
+static inline size_t _sg_copy_buffer(struct scatterlist *sgl,
+ unsigned int nents, void *buf,
+ size_t buflen, off_t skip,
+ bool to_buffer)
+{
+ unsigned int offset = 0;
+ struct sg_mapping_iter miter;
+ unsigned long flags;
+ unsigned int sg_flags = SG_MITER_ATOMIC;
+
+ if (to_buffer)
+ sg_flags |= SG_MITER_FROM_SG;
+ else
+ sg_flags |= SG_MITER_TO_SG;
+
+ sg_miter_start(&miter, sgl, nents, sg_flags);
+
+ if (!sg_miter_skip(&miter, skip))
+ return false;
+
+ local_irq_save(flags);
+
+ while (sg_miter_next(&miter) && offset < buflen) {
+ unsigned int len;
+
+ len = min(miter.length, buflen - offset);
+
+ if (to_buffer)
+ memcpy(buf + offset, miter.addr, len);
+ else
+ memcpy(miter.addr, buf + offset, len);
+
+ offset += len;
+ }
+
+ sg_miter_stop(&miter);
+
+ local_irq_restore(flags);
+ return offset;
+}
+
+static inline size_t sg_pcopy_to_buffer(struct scatterlist *sgl,
+ unsigned int nents, void *buf,
+ size_t buflen, off_t skip)
+{
+ return _sg_copy_buffer(sgl, nents, buf, buflen, skip, true);
+}
+
+static inline size_t sg_pcopy_from_buffer(struct scatterlist *sgl,
+ unsigned int nents, void *buf,
+ size_t buflen, off_t skip)
+{
+ return _sg_copy_buffer(sgl, nents, buf, buflen, skip, false);
+}
+#endif
+
#endif /* __GREYBUS_KERNEL_VER_H */