summaryrefslogtreecommitdiffstats
path: root/sys/dev/pci/drm/include/linux/scatterlist.h
blob: 43392adbba7f1bc5d483018d900ca2ed1a726871 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/*	$OpenBSD: scatterlist.h,v 1.3 2020/06/11 11:28:12 jsg Exp $	*/
/*
 * Copyright (c) 2013, 2014, 2015 Mark Kettenis
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#ifndef _LINUX_SCATTERLIST_H
#define _LINUX_SCATTERLIST_H

#include <sys/types.h>
#include <sys/param.h>
#include <uvm/uvm_extern.h>

#include <linux/mm.h>

struct scatterlist {
	struct vm_page *__page;
	dma_addr_t dma_address;
	unsigned int offset;
	unsigned int length;
	bool end;
};

struct sg_table {
	struct scatterlist *sgl;
	unsigned int nents;
	unsigned int orig_nents;
};

struct sg_page_iter {
	struct scatterlist *sg;
	unsigned int sg_pgoffset;
	unsigned int __nents;
};

#define sg_is_chain(sg)		false
#define sg_is_last(sg)		((sg)->end)
#define sg_chain_ptr(sg)	NULL

static inline struct scatterlist *
sg_next(struct scatterlist *sgl)
{
	return sg_is_last(sgl) ? NULL : ++sgl;
}

int sg_alloc_table(struct sg_table *, unsigned int, gfp_t);
void sg_free_table(struct sg_table *);

static inline void
sg_mark_end(struct scatterlist *sgl)
{
	sgl->end = true;
}

static inline void
__sg_page_iter_start(struct sg_page_iter *iter, struct scatterlist *sgl,
    unsigned int nents, unsigned long pgoffset)
{
	iter->sg = sgl;
	iter->sg_pgoffset = pgoffset - 1;
	iter->__nents = nents;
}

static inline bool
__sg_page_iter_next(struct sg_page_iter *iter)
{
	iter->sg_pgoffset++;
	while (iter->__nents > 0 && 
	    iter->sg_pgoffset >= (iter->sg->length / PAGE_SIZE)) {
		iter->sg_pgoffset -= (iter->sg->length / PAGE_SIZE);
		iter->sg++;
		iter->__nents--;
	}

	return (iter->__nents > 0);
}

static inline paddr_t
sg_page_iter_dma_address(struct sg_page_iter *iter)
{
	return iter->sg->dma_address + (iter->sg_pgoffset << PAGE_SHIFT);
}

static inline struct vm_page *
sg_page_iter_page(struct sg_page_iter *iter)
{
	return PHYS_TO_VM_PAGE(sg_page_iter_dma_address(iter));
}

static inline struct vm_page *
sg_page(struct scatterlist *sgl)
{
	return sgl->__page;
}

static inline void
sg_assign_page(struct scatterlist *sgl, struct vm_page *page)
{
	sgl->__page = page;
}

static inline void
sg_set_page(struct scatterlist *sgl, struct vm_page *page,
    unsigned int length, unsigned int offset)
{
	sgl->__page = page;
	sgl->dma_address = page ? VM_PAGE_TO_PHYS(page) : 0;
	sgl->offset = offset;
	sgl->length = length;
	sgl->end = false;
}

#define sg_dma_address(sg)	((sg)->dma_address)
#define sg_dma_len(sg)		((sg)->length)

#define for_each_sg(sgl, sg, nents, i) \
  for (i = 0, sg = (sgl); i < (nents); i++, sg = sg_next(sg))

#define for_each_sg_page(sgl, iter, nents, pgoffset) \
  __sg_page_iter_start((iter), (sgl), (nents), (pgoffset)); \
  while (__sg_page_iter_next(iter))

size_t sg_copy_from_buffer(struct scatterlist *, unsigned int,
    const void *, size_t);

#endif