Commit | Line | Data |
---|---|---|
d15bd7ee SS |
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); |