/* * mods_dmabuf.c - This file is part of NVIDIA MODS kernel driver. * * Copyright (c) 2014-2017, NVIDIA CORPORATION. All rights reserved. * * NVIDIA MODS kernel driver is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * NVIDIA MODS kernel driver is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with NVIDIA MODS kernel driver. * If not, see . */ #include #include "mods_config.h" #ifdef MODS_HAS_DMABUF #include #include #include "mods_internal.h" static void dummy_release(struct device *dev) { } static struct device_dma_parameters dma_parms = { .max_segment_size = UINT_MAX, }; static struct platform_device dummy_device = { .name = "nvidia_mods_dummy_device", .id = -1, .dev = { .dma_parms = &dma_parms, .release = dummy_release, }, }; static bool dummy_device_registered; int esc_mods_dmabuf_get_phys_addr(struct file *filp, struct MODS_DMABUF_GET_PHYSICAL_ADDRESS *op) { int err = 0; struct dma_buf *dmabuf = NULL; struct dma_buf_attachment *attachment = NULL; struct sg_table *sgt = NULL; unsigned int remaining_offset = op->offset; phys_addr_t physical_address = 0; unsigned int segment_size = 0; struct scatterlist *sg; unsigned int sg_index; if (op->offset > UINT_MAX) return -EINVAL; dmabuf = dma_buf_get(op->buf_fd); if (IS_ERR_OR_NULL(dmabuf)) return IS_ERR(dmabuf) ? PTR_ERR(dmabuf) : -EINVAL; attachment = dma_buf_attach(dmabuf, &dummy_device.dev); if (IS_ERR_OR_NULL(attachment)) { mods_error_printk("%s: failed to attach dma buf\n", __func__); err = IS_ERR(attachment) ? PTR_ERR(attachment) : -EFAULT; goto buf_attach_fail; } sgt = dma_buf_map_attachment(attachment, DMA_BIDIRECTIONAL); if (IS_ERR_OR_NULL(sgt)) { mods_error_printk("%s: failed to map dma buf\n", __func__); err = IS_ERR(sgt) ? PTR_ERR(sgt) : -EFAULT; goto buf_map_fail; } for_each_sg(sgt->sgl, sg, sgt->nents, sg_index) { if (remaining_offset >= sg->length) { /* haven't reached segment yet, or empty sg */ remaining_offset -= sg->length; } else if (segment_size == 0) { /* first sg in segment */ physical_address = sg_phys(sg) + remaining_offset; segment_size = sg->length - remaining_offset; remaining_offset = 0; } else if (sg_phys(sg) == physical_address + segment_size) { /* contiguous sg; append to segment */ segment_size += sg->length; } else { /* discontiguous sg; end segment */ break; } } if (segment_size == 0) { err = -EINVAL; } else { op->physical_address = physical_address; op->segment_size = segment_size; } dma_buf_unmap_attachment(attachment, sgt, DMA_BIDIRECTIONAL); buf_map_fail: dma_buf_detach(dmabuf, attachment); buf_attach_fail: dma_buf_put(dmabuf); return err; } int mods_init_dmabuf(void) { int ret; ret = platform_device_register(&dummy_device); if (ret) { mods_error_printk("failed to register %s\n", dummy_device.name); return ret; } dummy_device_registered = true; return 0; } void mods_exit_dmabuf(void) { if (dummy_device_registered) { platform_device_unregister(&dummy_device); dummy_device_registered = false; } } #endif