diff options
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/base/dma-buf.c | 99 |
1 files changed, 98 insertions, 1 deletions
diff --git a/drivers/base/dma-buf.c b/drivers/base/dma-buf.c index 05c64c11bad2..24e88fe29ec1 100644 --- a/drivers/base/dma-buf.c +++ b/drivers/base/dma-buf.c | |||
| @@ -44,8 +44,26 @@ static int dma_buf_release(struct inode *inode, struct file *file) | |||
| 44 | return 0; | 44 | return 0; |
| 45 | } | 45 | } |
| 46 | 46 | ||
| 47 | static int dma_buf_mmap_internal(struct file *file, struct vm_area_struct *vma) | ||
| 48 | { | ||
| 49 | struct dma_buf *dmabuf; | ||
| 50 | |||
| 51 | if (!is_dma_buf_file(file)) | ||
| 52 | return -EINVAL; | ||
| 53 | |||
| 54 | dmabuf = file->private_data; | ||
| 55 | |||
| 56 | /* check for overflowing the buffer's size */ | ||
| 57 | if (vma->vm_pgoff + ((vma->vm_end - vma->vm_start) >> PAGE_SHIFT) > | ||
| 58 | dmabuf->size >> PAGE_SHIFT) | ||
| 59 | return -EINVAL; | ||
| 60 | |||
| 61 | return dmabuf->ops->mmap(dmabuf, vma); | ||
| 62 | } | ||
| 63 | |||
| 47 | static const struct file_operations dma_buf_fops = { | 64 | static const struct file_operations dma_buf_fops = { |
| 48 | .release = dma_buf_release, | 65 | .release = dma_buf_release, |
| 66 | .mmap = dma_buf_mmap_internal, | ||
| 49 | }; | 67 | }; |
| 50 | 68 | ||
| 51 | /* | 69 | /* |
| @@ -82,7 +100,8 @@ struct dma_buf *dma_buf_export(void *priv, const struct dma_buf_ops *ops, | |||
| 82 | || !ops->unmap_dma_buf | 100 | || !ops->unmap_dma_buf |
| 83 | || !ops->release | 101 | || !ops->release |
| 84 | || !ops->kmap_atomic | 102 | || !ops->kmap_atomic |
| 85 | || !ops->kmap)) { | 103 | || !ops->kmap |
| 104 | || !ops->mmap)) { | ||
| 86 | return ERR_PTR(-EINVAL); | 105 | return ERR_PTR(-EINVAL); |
| 87 | } | 106 | } |
| 88 | 107 | ||
| @@ -406,3 +425,81 @@ void dma_buf_kunmap(struct dma_buf *dmabuf, unsigned long page_num, | |||
| 406 | dmabuf->ops->kunmap(dmabuf, page_num, vaddr); | 425 | dmabuf->ops->kunmap(dmabuf, page_num, vaddr); |
| 407 | } | 426 | } |
| 408 | EXPORT_SYMBOL_GPL(dma_buf_kunmap); | 427 | EXPORT_SYMBOL_GPL(dma_buf_kunmap); |
| 428 | |||
| 429 | |||
| 430 | /** | ||
| 431 | * dma_buf_mmap - Setup up a userspace mmap with the given vma | ||
| 432 | * @dmabuf: [in] buffer that should back the vma | ||
| 433 | * @vma: [in] vma for the mmap | ||
| 434 | * @pgoff: [in] offset in pages where this mmap should start within the | ||
| 435 | * dma-buf buffer. | ||
| 436 | * | ||
| 437 | * This function adjusts the passed in vma so that it points at the file of the | ||
| 438 | * dma_buf operation. It alsog adjusts the starting pgoff and does bounds | ||
| 439 | * checking on the size of the vma. Then it calls the exporters mmap function to | ||
| 440 | * set up the mapping. | ||
| 441 | * | ||
| 442 | * Can return negative error values, returns 0 on success. | ||
| 443 | */ | ||
| 444 | int dma_buf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma, | ||
| 445 | unsigned long pgoff) | ||
| 446 | { | ||
| 447 | if (WARN_ON(!dmabuf || !vma)) | ||
| 448 | return -EINVAL; | ||
| 449 | |||
| 450 | /* check for offset overflow */ | ||
| 451 | if (pgoff + ((vma->vm_end - vma->vm_start) >> PAGE_SHIFT) < pgoff) | ||
| 452 | return -EOVERFLOW; | ||
| 453 | |||
| 454 | /* check for overflowing the buffer's size */ | ||
| 455 | if (pgoff + ((vma->vm_end - vma->vm_start) >> PAGE_SHIFT) > | ||
| 456 | dmabuf->size >> PAGE_SHIFT) | ||
| 457 | return -EINVAL; | ||
| 458 | |||
| 459 | /* readjust the vma */ | ||
| 460 | if (vma->vm_file) | ||
| 461 | fput(vma->vm_file); | ||
| 462 | |||
| 463 | vma->vm_file = dmabuf->file; | ||
| 464 | get_file(vma->vm_file); | ||
| 465 | |||
| 466 | vma->vm_pgoff = pgoff; | ||
| 467 | |||
| 468 | return dmabuf->ops->mmap(dmabuf, vma); | ||
| 469 | } | ||
| 470 | EXPORT_SYMBOL_GPL(dma_buf_mmap); | ||
| 471 | |||
| 472 | /** | ||
| 473 | * dma_buf_vmap - Create virtual mapping for the buffer object into kernel | ||
| 474 | * address space. Same restrictions as for vmap and friends apply. | ||
| 475 | * @dmabuf: [in] buffer to vmap | ||
| 476 | * | ||
| 477 | * This call may fail due to lack of virtual mapping address space. | ||
| 478 | * These calls are optional in drivers. The intended use for them | ||
| 479 | * is for mapping objects linear in kernel space for high use objects. | ||
| 480 | * Please attempt to use kmap/kunmap before thinking about these interfaces. | ||
| 481 | */ | ||
| 482 | void *dma_buf_vmap(struct dma_buf *dmabuf) | ||
| 483 | { | ||
| 484 | if (WARN_ON(!dmabuf)) | ||
| 485 | return NULL; | ||
| 486 | |||
| 487 | if (dmabuf->ops->vmap) | ||
| 488 | return dmabuf->ops->vmap(dmabuf); | ||
| 489 | return NULL; | ||
| 490 | } | ||
| 491 | EXPORT_SYMBOL_GPL(dma_buf_vmap); | ||
| 492 | |||
| 493 | /** | ||
| 494 | * dma_buf_vunmap - Unmap a vmap obtained by dma_buf_vmap. | ||
| 495 | * @dmabuf: [in] buffer to vunmap | ||
| 496 | */ | ||
| 497 | void dma_buf_vunmap(struct dma_buf *dmabuf, void *vaddr) | ||
| 498 | { | ||
| 499 | if (WARN_ON(!dmabuf)) | ||
| 500 | return; | ||
| 501 | |||
| 502 | if (dmabuf->ops->vunmap) | ||
| 503 | dmabuf->ops->vunmap(dmabuf, vaddr); | ||
| 504 | } | ||
| 505 | EXPORT_SYMBOL_GPL(dma_buf_vunmap); | ||
