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