162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * A generic kernel FIFO implementation 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2009/2010 Stefani Seibold <stefani@seibold.net> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/export.h> 1062306a36Sopenharmony_ci#include <linux/slab.h> 1162306a36Sopenharmony_ci#include <linux/err.h> 1262306a36Sopenharmony_ci#include <linux/log2.h> 1362306a36Sopenharmony_ci#include <linux/uaccess.h> 1462306a36Sopenharmony_ci#include <linux/kfifo.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/* 1762306a36Sopenharmony_ci * internal helper to calculate the unused elements in a fifo 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_cistatic inline unsigned int kfifo_unused(struct __kfifo *fifo) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci return (fifo->mask + 1) - (fifo->in - fifo->out); 2262306a36Sopenharmony_ci} 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ciint __kfifo_alloc(struct __kfifo *fifo, unsigned int size, 2562306a36Sopenharmony_ci size_t esize, gfp_t gfp_mask) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci /* 2862306a36Sopenharmony_ci * round up to the next power of 2, since our 'let the indices 2962306a36Sopenharmony_ci * wrap' technique works only in this case. 3062306a36Sopenharmony_ci */ 3162306a36Sopenharmony_ci size = roundup_pow_of_two(size); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci fifo->in = 0; 3462306a36Sopenharmony_ci fifo->out = 0; 3562306a36Sopenharmony_ci fifo->esize = esize; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci if (size < 2) { 3862306a36Sopenharmony_ci fifo->data = NULL; 3962306a36Sopenharmony_ci fifo->mask = 0; 4062306a36Sopenharmony_ci return -EINVAL; 4162306a36Sopenharmony_ci } 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci fifo->data = kmalloc_array(esize, size, gfp_mask); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci if (!fifo->data) { 4662306a36Sopenharmony_ci fifo->mask = 0; 4762306a36Sopenharmony_ci return -ENOMEM; 4862306a36Sopenharmony_ci } 4962306a36Sopenharmony_ci fifo->mask = size - 1; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci return 0; 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ciEXPORT_SYMBOL(__kfifo_alloc); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_civoid __kfifo_free(struct __kfifo *fifo) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci kfree(fifo->data); 5862306a36Sopenharmony_ci fifo->in = 0; 5962306a36Sopenharmony_ci fifo->out = 0; 6062306a36Sopenharmony_ci fifo->esize = 0; 6162306a36Sopenharmony_ci fifo->data = NULL; 6262306a36Sopenharmony_ci fifo->mask = 0; 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ciEXPORT_SYMBOL(__kfifo_free); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ciint __kfifo_init(struct __kfifo *fifo, void *buffer, 6762306a36Sopenharmony_ci unsigned int size, size_t esize) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci size /= esize; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (!is_power_of_2(size)) 7262306a36Sopenharmony_ci size = rounddown_pow_of_two(size); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci fifo->in = 0; 7562306a36Sopenharmony_ci fifo->out = 0; 7662306a36Sopenharmony_ci fifo->esize = esize; 7762306a36Sopenharmony_ci fifo->data = buffer; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (size < 2) { 8062306a36Sopenharmony_ci fifo->mask = 0; 8162306a36Sopenharmony_ci return -EINVAL; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci fifo->mask = size - 1; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci return 0; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ciEXPORT_SYMBOL(__kfifo_init); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic void kfifo_copy_in(struct __kfifo *fifo, const void *src, 9062306a36Sopenharmony_ci unsigned int len, unsigned int off) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci unsigned int size = fifo->mask + 1; 9362306a36Sopenharmony_ci unsigned int esize = fifo->esize; 9462306a36Sopenharmony_ci unsigned int l; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci off &= fifo->mask; 9762306a36Sopenharmony_ci if (esize != 1) { 9862306a36Sopenharmony_ci off *= esize; 9962306a36Sopenharmony_ci size *= esize; 10062306a36Sopenharmony_ci len *= esize; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci l = min(len, size - off); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci memcpy(fifo->data + off, src, l); 10562306a36Sopenharmony_ci memcpy(fifo->data, src + l, len - l); 10662306a36Sopenharmony_ci /* 10762306a36Sopenharmony_ci * make sure that the data in the fifo is up to date before 10862306a36Sopenharmony_ci * incrementing the fifo->in index counter 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_ci smp_wmb(); 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ciunsigned int __kfifo_in(struct __kfifo *fifo, 11462306a36Sopenharmony_ci const void *buf, unsigned int len) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci unsigned int l; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci l = kfifo_unused(fifo); 11962306a36Sopenharmony_ci if (len > l) 12062306a36Sopenharmony_ci len = l; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci kfifo_copy_in(fifo, buf, len, fifo->in); 12362306a36Sopenharmony_ci fifo->in += len; 12462306a36Sopenharmony_ci return len; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ciEXPORT_SYMBOL(__kfifo_in); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic void kfifo_copy_out(struct __kfifo *fifo, void *dst, 12962306a36Sopenharmony_ci unsigned int len, unsigned int off) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci unsigned int size = fifo->mask + 1; 13262306a36Sopenharmony_ci unsigned int esize = fifo->esize; 13362306a36Sopenharmony_ci unsigned int l; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci off &= fifo->mask; 13662306a36Sopenharmony_ci if (esize != 1) { 13762306a36Sopenharmony_ci off *= esize; 13862306a36Sopenharmony_ci size *= esize; 13962306a36Sopenharmony_ci len *= esize; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci l = min(len, size - off); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci memcpy(dst, fifo->data + off, l); 14462306a36Sopenharmony_ci memcpy(dst + l, fifo->data, len - l); 14562306a36Sopenharmony_ci /* 14662306a36Sopenharmony_ci * make sure that the data is copied before 14762306a36Sopenharmony_ci * incrementing the fifo->out index counter 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_ci smp_wmb(); 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ciunsigned int __kfifo_out_peek(struct __kfifo *fifo, 15362306a36Sopenharmony_ci void *buf, unsigned int len) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci unsigned int l; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci l = fifo->in - fifo->out; 15862306a36Sopenharmony_ci if (len > l) 15962306a36Sopenharmony_ci len = l; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci kfifo_copy_out(fifo, buf, len, fifo->out); 16262306a36Sopenharmony_ci return len; 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ciEXPORT_SYMBOL(__kfifo_out_peek); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ciunsigned int __kfifo_out(struct __kfifo *fifo, 16762306a36Sopenharmony_ci void *buf, unsigned int len) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci len = __kfifo_out_peek(fifo, buf, len); 17062306a36Sopenharmony_ci fifo->out += len; 17162306a36Sopenharmony_ci return len; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ciEXPORT_SYMBOL(__kfifo_out); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic unsigned long kfifo_copy_from_user(struct __kfifo *fifo, 17662306a36Sopenharmony_ci const void __user *from, unsigned int len, unsigned int off, 17762306a36Sopenharmony_ci unsigned int *copied) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci unsigned int size = fifo->mask + 1; 18062306a36Sopenharmony_ci unsigned int esize = fifo->esize; 18162306a36Sopenharmony_ci unsigned int l; 18262306a36Sopenharmony_ci unsigned long ret; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci off &= fifo->mask; 18562306a36Sopenharmony_ci if (esize != 1) { 18662306a36Sopenharmony_ci off *= esize; 18762306a36Sopenharmony_ci size *= esize; 18862306a36Sopenharmony_ci len *= esize; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci l = min(len, size - off); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci ret = copy_from_user(fifo->data + off, from, l); 19362306a36Sopenharmony_ci if (unlikely(ret)) 19462306a36Sopenharmony_ci ret = DIV_ROUND_UP(ret + len - l, esize); 19562306a36Sopenharmony_ci else { 19662306a36Sopenharmony_ci ret = copy_from_user(fifo->data, from + l, len - l); 19762306a36Sopenharmony_ci if (unlikely(ret)) 19862306a36Sopenharmony_ci ret = DIV_ROUND_UP(ret, esize); 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci /* 20162306a36Sopenharmony_ci * make sure that the data in the fifo is up to date before 20262306a36Sopenharmony_ci * incrementing the fifo->in index counter 20362306a36Sopenharmony_ci */ 20462306a36Sopenharmony_ci smp_wmb(); 20562306a36Sopenharmony_ci *copied = len - ret * esize; 20662306a36Sopenharmony_ci /* return the number of elements which are not copied */ 20762306a36Sopenharmony_ci return ret; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ciint __kfifo_from_user(struct __kfifo *fifo, const void __user *from, 21162306a36Sopenharmony_ci unsigned long len, unsigned int *copied) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci unsigned int l; 21462306a36Sopenharmony_ci unsigned long ret; 21562306a36Sopenharmony_ci unsigned int esize = fifo->esize; 21662306a36Sopenharmony_ci int err; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (esize != 1) 21962306a36Sopenharmony_ci len /= esize; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci l = kfifo_unused(fifo); 22262306a36Sopenharmony_ci if (len > l) 22362306a36Sopenharmony_ci len = l; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci ret = kfifo_copy_from_user(fifo, from, len, fifo->in, copied); 22662306a36Sopenharmony_ci if (unlikely(ret)) { 22762306a36Sopenharmony_ci len -= ret; 22862306a36Sopenharmony_ci err = -EFAULT; 22962306a36Sopenharmony_ci } else 23062306a36Sopenharmony_ci err = 0; 23162306a36Sopenharmony_ci fifo->in += len; 23262306a36Sopenharmony_ci return err; 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ciEXPORT_SYMBOL(__kfifo_from_user); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic unsigned long kfifo_copy_to_user(struct __kfifo *fifo, void __user *to, 23762306a36Sopenharmony_ci unsigned int len, unsigned int off, unsigned int *copied) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci unsigned int l; 24062306a36Sopenharmony_ci unsigned long ret; 24162306a36Sopenharmony_ci unsigned int size = fifo->mask + 1; 24262306a36Sopenharmony_ci unsigned int esize = fifo->esize; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci off &= fifo->mask; 24562306a36Sopenharmony_ci if (esize != 1) { 24662306a36Sopenharmony_ci off *= esize; 24762306a36Sopenharmony_ci size *= esize; 24862306a36Sopenharmony_ci len *= esize; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci l = min(len, size - off); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci ret = copy_to_user(to, fifo->data + off, l); 25362306a36Sopenharmony_ci if (unlikely(ret)) 25462306a36Sopenharmony_ci ret = DIV_ROUND_UP(ret + len - l, esize); 25562306a36Sopenharmony_ci else { 25662306a36Sopenharmony_ci ret = copy_to_user(to + l, fifo->data, len - l); 25762306a36Sopenharmony_ci if (unlikely(ret)) 25862306a36Sopenharmony_ci ret = DIV_ROUND_UP(ret, esize); 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci /* 26162306a36Sopenharmony_ci * make sure that the data is copied before 26262306a36Sopenharmony_ci * incrementing the fifo->out index counter 26362306a36Sopenharmony_ci */ 26462306a36Sopenharmony_ci smp_wmb(); 26562306a36Sopenharmony_ci *copied = len - ret * esize; 26662306a36Sopenharmony_ci /* return the number of elements which are not copied */ 26762306a36Sopenharmony_ci return ret; 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ciint __kfifo_to_user(struct __kfifo *fifo, void __user *to, 27162306a36Sopenharmony_ci unsigned long len, unsigned int *copied) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci unsigned int l; 27462306a36Sopenharmony_ci unsigned long ret; 27562306a36Sopenharmony_ci unsigned int esize = fifo->esize; 27662306a36Sopenharmony_ci int err; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (esize != 1) 27962306a36Sopenharmony_ci len /= esize; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci l = fifo->in - fifo->out; 28262306a36Sopenharmony_ci if (len > l) 28362306a36Sopenharmony_ci len = l; 28462306a36Sopenharmony_ci ret = kfifo_copy_to_user(fifo, to, len, fifo->out, copied); 28562306a36Sopenharmony_ci if (unlikely(ret)) { 28662306a36Sopenharmony_ci len -= ret; 28762306a36Sopenharmony_ci err = -EFAULT; 28862306a36Sopenharmony_ci } else 28962306a36Sopenharmony_ci err = 0; 29062306a36Sopenharmony_ci fifo->out += len; 29162306a36Sopenharmony_ci return err; 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ciEXPORT_SYMBOL(__kfifo_to_user); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic int setup_sgl_buf(struct scatterlist *sgl, void *buf, 29662306a36Sopenharmony_ci int nents, unsigned int len) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci int n; 29962306a36Sopenharmony_ci unsigned int l; 30062306a36Sopenharmony_ci unsigned int off; 30162306a36Sopenharmony_ci struct page *page; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci if (!nents) 30462306a36Sopenharmony_ci return 0; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (!len) 30762306a36Sopenharmony_ci return 0; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci n = 0; 31062306a36Sopenharmony_ci page = virt_to_page(buf); 31162306a36Sopenharmony_ci off = offset_in_page(buf); 31262306a36Sopenharmony_ci l = 0; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci while (len >= l + PAGE_SIZE - off) { 31562306a36Sopenharmony_ci struct page *npage; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci l += PAGE_SIZE; 31862306a36Sopenharmony_ci buf += PAGE_SIZE; 31962306a36Sopenharmony_ci npage = virt_to_page(buf); 32062306a36Sopenharmony_ci if (page_to_phys(page) != page_to_phys(npage) - l) { 32162306a36Sopenharmony_ci sg_set_page(sgl, page, l - off, off); 32262306a36Sopenharmony_ci sgl = sg_next(sgl); 32362306a36Sopenharmony_ci if (++n == nents || sgl == NULL) 32462306a36Sopenharmony_ci return n; 32562306a36Sopenharmony_ci page = npage; 32662306a36Sopenharmony_ci len -= l - off; 32762306a36Sopenharmony_ci l = off = 0; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci sg_set_page(sgl, page, len, off); 33162306a36Sopenharmony_ci return n + 1; 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic unsigned int setup_sgl(struct __kfifo *fifo, struct scatterlist *sgl, 33562306a36Sopenharmony_ci int nents, unsigned int len, unsigned int off) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci unsigned int size = fifo->mask + 1; 33862306a36Sopenharmony_ci unsigned int esize = fifo->esize; 33962306a36Sopenharmony_ci unsigned int l; 34062306a36Sopenharmony_ci unsigned int n; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci off &= fifo->mask; 34362306a36Sopenharmony_ci if (esize != 1) { 34462306a36Sopenharmony_ci off *= esize; 34562306a36Sopenharmony_ci size *= esize; 34662306a36Sopenharmony_ci len *= esize; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci l = min(len, size - off); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci n = setup_sgl_buf(sgl, fifo->data + off, nents, l); 35162306a36Sopenharmony_ci n += setup_sgl_buf(sgl + n, fifo->data, nents - n, len - l); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci return n; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ciunsigned int __kfifo_dma_in_prepare(struct __kfifo *fifo, 35762306a36Sopenharmony_ci struct scatterlist *sgl, int nents, unsigned int len) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci unsigned int l; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci l = kfifo_unused(fifo); 36262306a36Sopenharmony_ci if (len > l) 36362306a36Sopenharmony_ci len = l; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci return setup_sgl(fifo, sgl, nents, len, fifo->in); 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ciEXPORT_SYMBOL(__kfifo_dma_in_prepare); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ciunsigned int __kfifo_dma_out_prepare(struct __kfifo *fifo, 37062306a36Sopenharmony_ci struct scatterlist *sgl, int nents, unsigned int len) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci unsigned int l; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci l = fifo->in - fifo->out; 37562306a36Sopenharmony_ci if (len > l) 37662306a36Sopenharmony_ci len = l; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci return setup_sgl(fifo, sgl, nents, len, fifo->out); 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ciEXPORT_SYMBOL(__kfifo_dma_out_prepare); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ciunsigned int __kfifo_max_r(unsigned int len, size_t recsize) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci unsigned int max = (1 << (recsize << 3)) - 1; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (len > max) 38762306a36Sopenharmony_ci return max; 38862306a36Sopenharmony_ci return len; 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ciEXPORT_SYMBOL(__kfifo_max_r); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci#define __KFIFO_PEEK(data, out, mask) \ 39362306a36Sopenharmony_ci ((data)[(out) & (mask)]) 39462306a36Sopenharmony_ci/* 39562306a36Sopenharmony_ci * __kfifo_peek_n internal helper function for determinate the length of 39662306a36Sopenharmony_ci * the next record in the fifo 39762306a36Sopenharmony_ci */ 39862306a36Sopenharmony_cistatic unsigned int __kfifo_peek_n(struct __kfifo *fifo, size_t recsize) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci unsigned int l; 40162306a36Sopenharmony_ci unsigned int mask = fifo->mask; 40262306a36Sopenharmony_ci unsigned char *data = fifo->data; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci l = __KFIFO_PEEK(data, fifo->out, mask); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if (--recsize) 40762306a36Sopenharmony_ci l |= __KFIFO_PEEK(data, fifo->out + 1, mask) << 8; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci return l; 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci#define __KFIFO_POKE(data, in, mask, val) \ 41362306a36Sopenharmony_ci ( \ 41462306a36Sopenharmony_ci (data)[(in) & (mask)] = (unsigned char)(val) \ 41562306a36Sopenharmony_ci ) 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci/* 41862306a36Sopenharmony_ci * __kfifo_poke_n internal helper function for storing the length of 41962306a36Sopenharmony_ci * the record into the fifo 42062306a36Sopenharmony_ci */ 42162306a36Sopenharmony_cistatic void __kfifo_poke_n(struct __kfifo *fifo, unsigned int n, size_t recsize) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci unsigned int mask = fifo->mask; 42462306a36Sopenharmony_ci unsigned char *data = fifo->data; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci __KFIFO_POKE(data, fifo->in, mask, n); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if (recsize > 1) 42962306a36Sopenharmony_ci __KFIFO_POKE(data, fifo->in + 1, mask, n >> 8); 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ciunsigned int __kfifo_len_r(struct __kfifo *fifo, size_t recsize) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci return __kfifo_peek_n(fifo, recsize); 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ciEXPORT_SYMBOL(__kfifo_len_r); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ciunsigned int __kfifo_in_r(struct __kfifo *fifo, const void *buf, 43962306a36Sopenharmony_ci unsigned int len, size_t recsize) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci if (len + recsize > kfifo_unused(fifo)) 44262306a36Sopenharmony_ci return 0; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci __kfifo_poke_n(fifo, len, recsize); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci kfifo_copy_in(fifo, buf, len, fifo->in + recsize); 44762306a36Sopenharmony_ci fifo->in += len + recsize; 44862306a36Sopenharmony_ci return len; 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ciEXPORT_SYMBOL(__kfifo_in_r); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cistatic unsigned int kfifo_out_copy_r(struct __kfifo *fifo, 45362306a36Sopenharmony_ci void *buf, unsigned int len, size_t recsize, unsigned int *n) 45462306a36Sopenharmony_ci{ 45562306a36Sopenharmony_ci *n = __kfifo_peek_n(fifo, recsize); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci if (len > *n) 45862306a36Sopenharmony_ci len = *n; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci kfifo_copy_out(fifo, buf, len, fifo->out + recsize); 46162306a36Sopenharmony_ci return len; 46262306a36Sopenharmony_ci} 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ciunsigned int __kfifo_out_peek_r(struct __kfifo *fifo, void *buf, 46562306a36Sopenharmony_ci unsigned int len, size_t recsize) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci unsigned int n; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci if (fifo->in == fifo->out) 47062306a36Sopenharmony_ci return 0; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci return kfifo_out_copy_r(fifo, buf, len, recsize, &n); 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ciEXPORT_SYMBOL(__kfifo_out_peek_r); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ciunsigned int __kfifo_out_r(struct __kfifo *fifo, void *buf, 47762306a36Sopenharmony_ci unsigned int len, size_t recsize) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci unsigned int n; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci if (fifo->in == fifo->out) 48262306a36Sopenharmony_ci return 0; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci len = kfifo_out_copy_r(fifo, buf, len, recsize, &n); 48562306a36Sopenharmony_ci fifo->out += n + recsize; 48662306a36Sopenharmony_ci return len; 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ciEXPORT_SYMBOL(__kfifo_out_r); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_civoid __kfifo_skip_r(struct __kfifo *fifo, size_t recsize) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci unsigned int n; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci n = __kfifo_peek_n(fifo, recsize); 49562306a36Sopenharmony_ci fifo->out += n + recsize; 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ciEXPORT_SYMBOL(__kfifo_skip_r); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ciint __kfifo_from_user_r(struct __kfifo *fifo, const void __user *from, 50062306a36Sopenharmony_ci unsigned long len, unsigned int *copied, size_t recsize) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci unsigned long ret; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci len = __kfifo_max_r(len, recsize); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci if (len + recsize > kfifo_unused(fifo)) { 50762306a36Sopenharmony_ci *copied = 0; 50862306a36Sopenharmony_ci return 0; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci __kfifo_poke_n(fifo, len, recsize); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci ret = kfifo_copy_from_user(fifo, from, len, fifo->in + recsize, copied); 51462306a36Sopenharmony_ci if (unlikely(ret)) { 51562306a36Sopenharmony_ci *copied = 0; 51662306a36Sopenharmony_ci return -EFAULT; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci fifo->in += len + recsize; 51962306a36Sopenharmony_ci return 0; 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ciEXPORT_SYMBOL(__kfifo_from_user_r); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ciint __kfifo_to_user_r(struct __kfifo *fifo, void __user *to, 52462306a36Sopenharmony_ci unsigned long len, unsigned int *copied, size_t recsize) 52562306a36Sopenharmony_ci{ 52662306a36Sopenharmony_ci unsigned long ret; 52762306a36Sopenharmony_ci unsigned int n; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci if (fifo->in == fifo->out) { 53062306a36Sopenharmony_ci *copied = 0; 53162306a36Sopenharmony_ci return 0; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci n = __kfifo_peek_n(fifo, recsize); 53562306a36Sopenharmony_ci if (len > n) 53662306a36Sopenharmony_ci len = n; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci ret = kfifo_copy_to_user(fifo, to, len, fifo->out + recsize, copied); 53962306a36Sopenharmony_ci if (unlikely(ret)) { 54062306a36Sopenharmony_ci *copied = 0; 54162306a36Sopenharmony_ci return -EFAULT; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci fifo->out += n + recsize; 54462306a36Sopenharmony_ci return 0; 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ciEXPORT_SYMBOL(__kfifo_to_user_r); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ciunsigned int __kfifo_dma_in_prepare_r(struct __kfifo *fifo, 54962306a36Sopenharmony_ci struct scatterlist *sgl, int nents, unsigned int len, size_t recsize) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci BUG_ON(!nents); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci len = __kfifo_max_r(len, recsize); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci if (len + recsize > kfifo_unused(fifo)) 55662306a36Sopenharmony_ci return 0; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci return setup_sgl(fifo, sgl, nents, len, fifo->in + recsize); 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ciEXPORT_SYMBOL(__kfifo_dma_in_prepare_r); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_civoid __kfifo_dma_in_finish_r(struct __kfifo *fifo, 56362306a36Sopenharmony_ci unsigned int len, size_t recsize) 56462306a36Sopenharmony_ci{ 56562306a36Sopenharmony_ci len = __kfifo_max_r(len, recsize); 56662306a36Sopenharmony_ci __kfifo_poke_n(fifo, len, recsize); 56762306a36Sopenharmony_ci fifo->in += len + recsize; 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ciEXPORT_SYMBOL(__kfifo_dma_in_finish_r); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ciunsigned int __kfifo_dma_out_prepare_r(struct __kfifo *fifo, 57262306a36Sopenharmony_ci struct scatterlist *sgl, int nents, unsigned int len, size_t recsize) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci BUG_ON(!nents); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci len = __kfifo_max_r(len, recsize); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (len + recsize > fifo->in - fifo->out) 57962306a36Sopenharmony_ci return 0; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci return setup_sgl(fifo, sgl, nents, len, fifo->out + recsize); 58262306a36Sopenharmony_ci} 58362306a36Sopenharmony_ciEXPORT_SYMBOL(__kfifo_dma_out_prepare_r); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_civoid __kfifo_dma_out_finish_r(struct __kfifo *fifo, size_t recsize) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci unsigned int len; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci len = __kfifo_peek_n(fifo, recsize); 59062306a36Sopenharmony_ci fifo->out += len + recsize; 59162306a36Sopenharmony_ci} 59262306a36Sopenharmony_ciEXPORT_SYMBOL(__kfifo_dma_out_finish_r); 593