scatterlist.h (4262B)
1/* SPDX-License-Identifier: GPL-2.0 */ 2#ifndef SCATTERLIST_H 3#define SCATTERLIST_H 4#include <linux/kernel.h> 5 6struct scatterlist { 7 unsigned long page_link; 8 unsigned int offset; 9 unsigned int length; 10 dma_addr_t dma_address; 11}; 12 13/* Scatterlist helpers, stolen from linux/scatterlist.h */ 14#define sg_is_chain(sg) ((sg)->page_link & 0x01) 15#define sg_is_last(sg) ((sg)->page_link & 0x02) 16#define sg_chain_ptr(sg) \ 17 ((struct scatterlist *) ((sg)->page_link & ~0x03)) 18 19/** 20 * sg_assign_page - Assign a given page to an SG entry 21 * @sg: SG entry 22 * @page: The page 23 * 24 * Description: 25 * Assign page to sg entry. Also see sg_set_page(), the most commonly used 26 * variant. 27 * 28 **/ 29static inline void sg_assign_page(struct scatterlist *sg, struct page *page) 30{ 31 unsigned long page_link = sg->page_link & 0x3; 32 33 /* 34 * In order for the low bit stealing approach to work, pages 35 * must be aligned at a 32-bit boundary as a minimum. 36 */ 37 BUG_ON((unsigned long) page & 0x03); 38#ifdef CONFIG_DEBUG_SG 39 BUG_ON(sg_is_chain(sg)); 40#endif 41 sg->page_link = page_link | (unsigned long) page; 42} 43 44/** 45 * sg_set_page - Set sg entry to point at given page 46 * @sg: SG entry 47 * @page: The page 48 * @len: Length of data 49 * @offset: Offset into page 50 * 51 * Description: 52 * Use this function to set an sg entry pointing at a page, never assign 53 * the page directly. We encode sg table information in the lower bits 54 * of the page pointer. See sg_page() for looking up the page belonging 55 * to an sg entry. 56 * 57 **/ 58static inline void sg_set_page(struct scatterlist *sg, struct page *page, 59 unsigned int len, unsigned int offset) 60{ 61 sg_assign_page(sg, page); 62 sg->offset = offset; 63 sg->length = len; 64} 65 66static inline struct page *sg_page(struct scatterlist *sg) 67{ 68#ifdef CONFIG_DEBUG_SG 69 BUG_ON(sg_is_chain(sg)); 70#endif 71 return (struct page *)((sg)->page_link & ~0x3); 72} 73 74/* 75 * Loop over each sg element, following the pointer to a new list if necessary 76 */ 77#define for_each_sg(sglist, sg, nr, __i) \ 78 for (__i = 0, sg = (sglist); __i < (nr); __i++, sg = sg_next(sg)) 79 80/** 81 * sg_chain - Chain two sglists together 82 * @prv: First scatterlist 83 * @prv_nents: Number of entries in prv 84 * @sgl: Second scatterlist 85 * 86 * Description: 87 * Links @prv@ and @sgl@ together, to form a longer scatterlist. 88 * 89 **/ 90static inline void sg_chain(struct scatterlist *prv, unsigned int prv_nents, 91 struct scatterlist *sgl) 92{ 93 /* 94 * offset and length are unused for chain entry. Clear them. 95 */ 96 prv[prv_nents - 1].offset = 0; 97 prv[prv_nents - 1].length = 0; 98 99 /* 100 * Set lowest bit to indicate a link pointer, and make sure to clear 101 * the termination bit if it happens to be set. 102 */ 103 prv[prv_nents - 1].page_link = ((unsigned long) sgl | 0x01) & ~0x02; 104} 105 106/** 107 * sg_mark_end - Mark the end of the scatterlist 108 * @sg: SG entryScatterlist 109 * 110 * Description: 111 * Marks the passed in sg entry as the termination point for the sg 112 * table. A call to sg_next() on this entry will return NULL. 113 * 114 **/ 115static inline void sg_mark_end(struct scatterlist *sg) 116{ 117 /* 118 * Set termination bit, clear potential chain bit 119 */ 120 sg->page_link |= 0x02; 121 sg->page_link &= ~0x01; 122} 123 124/** 125 * sg_unmark_end - Undo setting the end of the scatterlist 126 * @sg: SG entryScatterlist 127 * 128 * Description: 129 * Removes the termination marker from the given entry of the scatterlist. 130 * 131 **/ 132static inline void sg_unmark_end(struct scatterlist *sg) 133{ 134 sg->page_link &= ~0x02; 135} 136 137static inline struct scatterlist *sg_next(struct scatterlist *sg) 138{ 139 if (sg_is_last(sg)) 140 return NULL; 141 142 sg++; 143 if (unlikely(sg_is_chain(sg))) 144 sg = sg_chain_ptr(sg); 145 146 return sg; 147} 148 149static inline void sg_init_table(struct scatterlist *sgl, unsigned int nents) 150{ 151 memset(sgl, 0, sizeof(*sgl) * nents); 152 sg_mark_end(&sgl[nents - 1]); 153} 154 155static inline dma_addr_t sg_phys(struct scatterlist *sg) 156{ 157 return page_to_phys(sg_page(sg)) + sg->offset; 158} 159 160static inline void sg_set_buf(struct scatterlist *sg, const void *buf, 161 unsigned int buflen) 162{ 163 sg_set_page(sg, virt_to_page(buf), buflen, offset_in_page(buf)); 164} 165 166static inline void sg_init_one(struct scatterlist *sg, 167 const void *buf, unsigned int buflen) 168{ 169 sg_init_table(sg, 1); 170 sg_set_buf(sg, buf, buflen); 171} 172#endif /* SCATTERLIST_H */