diff options
Diffstat (limited to 'drivers/base/dma-buf.c')
-rw-r--r-- | drivers/base/dma-buf.c | 291 |
1 files changed, 291 insertions, 0 deletions
diff --git a/drivers/base/dma-buf.c b/drivers/base/dma-buf.c new file mode 100644 index 000000000000..e38ad243b4bb --- /dev/null +++ b/drivers/base/dma-buf.c | |||
@@ -0,0 +1,291 @@ | |||
1 | /* | ||
2 | * Framework for buffer objects that can be shared across devices/subsystems. | ||
3 | * | ||
4 | * Copyright(C) 2011 Linaro Limited. All rights reserved. | ||
5 | * Author: Sumit Semwal <sumit.semwal@ti.com> | ||
6 | * | ||
7 | * Many thanks to linaro-mm-sig list, and specially | ||
8 | * Arnd Bergmann <arnd@arndb.de>, Rob Clark <rob@ti.com> and | ||
9 | * Daniel Vetter <daniel@ffwll.ch> for their support in creation and | ||
10 | * refining of this idea. | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify it | ||
13 | * under the terms of the GNU General Public License version 2 as published by | ||
14 | * the Free Software Foundation. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
17 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
18 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
19 | * more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License along with | ||
22 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
23 | */ | ||
24 | |||
25 | #include <linux/fs.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/dma-buf.h> | ||
28 | #include <linux/anon_inodes.h> | ||
29 | #include <linux/export.h> | ||
30 | |||
31 | static inline int is_dma_buf_file(struct file *); | ||
32 | |||
33 | static int dma_buf_release(struct inode *inode, struct file *file) | ||
34 | { | ||
35 | struct dma_buf *dmabuf; | ||
36 | |||
37 | if (!is_dma_buf_file(file)) | ||
38 | return -EINVAL; | ||
39 | |||
40 | dmabuf = file->private_data; | ||
41 | |||
42 | dmabuf->ops->release(dmabuf); | ||
43 | kfree(dmabuf); | ||
44 | return 0; | ||
45 | } | ||
46 | |||
47 | static const struct file_operations dma_buf_fops = { | ||
48 | .release = dma_buf_release, | ||
49 | }; | ||
50 | |||
51 | /* | ||
52 | * is_dma_buf_file - Check if struct file* is associated with dma_buf | ||
53 | */ | ||
54 | static inline int is_dma_buf_file(struct file *file) | ||
55 | { | ||
56 | return file->f_op == &dma_buf_fops; | ||
57 | } | ||
58 | |||
59 | /** | ||
60 | * dma_buf_export - Creates a new dma_buf, and associates an anon file | ||
61 | * with this buffer, so it can be exported. | ||
62 | * Also connect the allocator specific data and ops to the buffer. | ||
63 | * | ||
64 | * @priv: [in] Attach private data of allocator to this buffer | ||
65 | * @ops: [in] Attach allocator-defined dma buf ops to the new buffer. | ||
66 | * @size: [in] Size of the buffer | ||
67 | * @flags: [in] mode flags for the file. | ||
68 | * | ||
69 | * Returns, on success, a newly created dma_buf object, which wraps the | ||
70 | * supplied private data and operations for dma_buf_ops. On either missing | ||
71 | * ops, or error in allocating struct dma_buf, will return negative error. | ||
72 | * | ||
73 | */ | ||
74 | struct dma_buf *dma_buf_export(void *priv, struct dma_buf_ops *ops, | ||
75 | size_t size, int flags) | ||
76 | { | ||
77 | struct dma_buf *dmabuf; | ||
78 | struct file *file; | ||
79 | |||
80 | if (WARN_ON(!priv || !ops | ||
81 | || !ops->map_dma_buf | ||
82 | || !ops->unmap_dma_buf | ||
83 | || !ops->release)) { | ||
84 | return ERR_PTR(-EINVAL); | ||
85 | } | ||
86 | |||
87 | dmabuf = kzalloc(sizeof(struct dma_buf), GFP_KERNEL); | ||
88 | if (dmabuf == NULL) | ||
89 | return ERR_PTR(-ENOMEM); | ||
90 | |||
91 | dmabuf->priv = priv; | ||
92 | dmabuf->ops = ops; | ||
93 | dmabuf->size = size; | ||
94 | |||
95 | file = anon_inode_getfile("dmabuf", &dma_buf_fops, dmabuf, flags); | ||
96 | |||
97 | dmabuf->file = file; | ||
98 | |||
99 | mutex_init(&dmabuf->lock); | ||
100 | INIT_LIST_HEAD(&dmabuf->attachments); | ||
101 | |||
102 | return dmabuf; | ||
103 | } | ||
104 | EXPORT_SYMBOL_GPL(dma_buf_export); | ||
105 | |||
106 | |||
107 | /** | ||
108 | * dma_buf_fd - returns a file descriptor for the given dma_buf | ||
109 | * @dmabuf: [in] pointer to dma_buf for which fd is required. | ||
110 | * | ||
111 | * On success, returns an associated 'fd'. Else, returns error. | ||
112 | */ | ||
113 | int dma_buf_fd(struct dma_buf *dmabuf) | ||
114 | { | ||
115 | int error, fd; | ||
116 | |||
117 | if (!dmabuf || !dmabuf->file) | ||
118 | return -EINVAL; | ||
119 | |||
120 | error = get_unused_fd(); | ||
121 | if (error < 0) | ||
122 | return error; | ||
123 | fd = error; | ||
124 | |||
125 | fd_install(fd, dmabuf->file); | ||
126 | |||
127 | return fd; | ||
128 | } | ||
129 | EXPORT_SYMBOL_GPL(dma_buf_fd); | ||
130 | |||
131 | /** | ||
132 | * dma_buf_get - returns the dma_buf structure related to an fd | ||
133 | * @fd: [in] fd associated with the dma_buf to be returned | ||
134 | * | ||
135 | * On success, returns the dma_buf structure associated with an fd; uses | ||
136 | * file's refcounting done by fget to increase refcount. returns ERR_PTR | ||
137 | * otherwise. | ||
138 | */ | ||
139 | struct dma_buf *dma_buf_get(int fd) | ||
140 | { | ||
141 | struct file *file; | ||
142 | |||
143 | file = fget(fd); | ||
144 | |||
145 | if (!file) | ||
146 | return ERR_PTR(-EBADF); | ||
147 | |||
148 | if (!is_dma_buf_file(file)) { | ||
149 | fput(file); | ||
150 | return ERR_PTR(-EINVAL); | ||
151 | } | ||
152 | |||
153 | return file->private_data; | ||
154 | } | ||
155 | EXPORT_SYMBOL_GPL(dma_buf_get); | ||
156 | |||
157 | /** | ||
158 | * dma_buf_put - decreases refcount of the buffer | ||
159 | * @dmabuf: [in] buffer to reduce refcount of | ||
160 | * | ||
161 | * Uses file's refcounting done implicitly by fput() | ||
162 | */ | ||
163 | void dma_buf_put(struct dma_buf *dmabuf) | ||
164 | { | ||
165 | if (WARN_ON(!dmabuf || !dmabuf->file)) | ||
166 | return; | ||
167 | |||
168 | fput(dmabuf->file); | ||
169 | } | ||
170 | EXPORT_SYMBOL_GPL(dma_buf_put); | ||
171 | |||
172 | /** | ||
173 | * dma_buf_attach - Add the device to dma_buf's attachments list; optionally, | ||
174 | * calls attach() of dma_buf_ops to allow device-specific attach functionality | ||
175 | * @dmabuf: [in] buffer to attach device to. | ||
176 | * @dev: [in] device to be attached. | ||
177 | * | ||
178 | * Returns struct dma_buf_attachment * for this attachment; may return negative | ||
179 | * error codes. | ||
180 | * | ||
181 | */ | ||
182 | struct dma_buf_attachment *dma_buf_attach(struct dma_buf *dmabuf, | ||
183 | struct device *dev) | ||
184 | { | ||
185 | struct dma_buf_attachment *attach; | ||
186 | int ret; | ||
187 | |||
188 | if (WARN_ON(!dmabuf || !dev || !dmabuf->ops)) | ||
189 | return ERR_PTR(-EINVAL); | ||
190 | |||
191 | attach = kzalloc(sizeof(struct dma_buf_attachment), GFP_KERNEL); | ||
192 | if (attach == NULL) | ||
193 | goto err_alloc; | ||
194 | |||
195 | mutex_lock(&dmabuf->lock); | ||
196 | |||
197 | attach->dev = dev; | ||
198 | attach->dmabuf = dmabuf; | ||
199 | if (dmabuf->ops->attach) { | ||
200 | ret = dmabuf->ops->attach(dmabuf, dev, attach); | ||
201 | if (ret) | ||
202 | goto err_attach; | ||
203 | } | ||
204 | list_add(&attach->node, &dmabuf->attachments); | ||
205 | |||
206 | mutex_unlock(&dmabuf->lock); | ||
207 | return attach; | ||
208 | |||
209 | err_alloc: | ||
210 | return ERR_PTR(-ENOMEM); | ||
211 | err_attach: | ||
212 | kfree(attach); | ||
213 | mutex_unlock(&dmabuf->lock); | ||
214 | return ERR_PTR(ret); | ||
215 | } | ||
216 | EXPORT_SYMBOL_GPL(dma_buf_attach); | ||
217 | |||
218 | /** | ||
219 | * dma_buf_detach - Remove the given attachment from dmabuf's attachments list; | ||
220 | * optionally calls detach() of dma_buf_ops for device-specific detach | ||
221 | * @dmabuf: [in] buffer to detach from. | ||
222 | * @attach: [in] attachment to be detached; is free'd after this call. | ||
223 | * | ||
224 | */ | ||
225 | void dma_buf_detach(struct dma_buf *dmabuf, struct dma_buf_attachment *attach) | ||
226 | { | ||
227 | if (WARN_ON(!dmabuf || !attach || !dmabuf->ops)) | ||
228 | return; | ||
229 | |||
230 | mutex_lock(&dmabuf->lock); | ||
231 | list_del(&attach->node); | ||
232 | if (dmabuf->ops->detach) | ||
233 | dmabuf->ops->detach(dmabuf, attach); | ||
234 | |||
235 | mutex_unlock(&dmabuf->lock); | ||
236 | kfree(attach); | ||
237 | } | ||
238 | EXPORT_SYMBOL_GPL(dma_buf_detach); | ||
239 | |||
240 | /** | ||
241 | * dma_buf_map_attachment - Returns the scatterlist table of the attachment; | ||
242 | * mapped into _device_ address space. Is a wrapper for map_dma_buf() of the | ||
243 | * dma_buf_ops. | ||
244 | * @attach: [in] attachment whose scatterlist is to be returned | ||
245 | * @direction: [in] direction of DMA transfer | ||
246 | * | ||
247 | * Returns sg_table containing the scatterlist to be returned; may return NULL | ||
248 | * or ERR_PTR. | ||
249 | * | ||
250 | */ | ||
251 | struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *attach, | ||
252 | enum dma_data_direction direction) | ||
253 | { | ||
254 | struct sg_table *sg_table = ERR_PTR(-EINVAL); | ||
255 | |||
256 | might_sleep(); | ||
257 | |||
258 | if (WARN_ON(!attach || !attach->dmabuf || !attach->dmabuf->ops)) | ||
259 | return ERR_PTR(-EINVAL); | ||
260 | |||
261 | mutex_lock(&attach->dmabuf->lock); | ||
262 | if (attach->dmabuf->ops->map_dma_buf) | ||
263 | sg_table = attach->dmabuf->ops->map_dma_buf(attach, direction); | ||
264 | mutex_unlock(&attach->dmabuf->lock); | ||
265 | |||
266 | return sg_table; | ||
267 | } | ||
268 | EXPORT_SYMBOL_GPL(dma_buf_map_attachment); | ||
269 | |||
270 | /** | ||
271 | * dma_buf_unmap_attachment - unmaps and decreases usecount of the buffer;might | ||
272 | * deallocate the scatterlist associated. Is a wrapper for unmap_dma_buf() of | ||
273 | * dma_buf_ops. | ||
274 | * @attach: [in] attachment to unmap buffer from | ||
275 | * @sg_table: [in] scatterlist info of the buffer to unmap | ||
276 | * | ||
277 | */ | ||
278 | void dma_buf_unmap_attachment(struct dma_buf_attachment *attach, | ||
279 | struct sg_table *sg_table) | ||
280 | { | ||
281 | if (WARN_ON(!attach || !attach->dmabuf || !sg_table | ||
282 | || !attach->dmabuf->ops)) | ||
283 | return; | ||
284 | |||
285 | mutex_lock(&attach->dmabuf->lock); | ||
286 | if (attach->dmabuf->ops->unmap_dma_buf) | ||
287 | attach->dmabuf->ops->unmap_dma_buf(attach, sg_table); | ||
288 | mutex_unlock(&attach->dmabuf->lock); | ||
289 | |||
290 | } | ||
291 | EXPORT_SYMBOL_GPL(dma_buf_unmap_attachment); | ||