blob: 3f0854a52803c7093439f7668a7ca3cbc81aedd3 [file]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/scatterlist.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/dma-buf.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/mm.h>
#include <linux/compat.h>
#include <linux/amlogic/tee.h>
#include <linux/amlogic/media/codec_mm/codec_mm.h>
#include <linux/amlogic/media/codec_mm/dmabuf_manage.h>
#include <linux/amlogic/media/codec_mm/configs.h>
#if IS_ENABLED(CONFIG_AMLOGIC_OPTEE)
#include <linux/amlogic/tee_drv.h>
#endif
#include <uapi/linux/dvb/aml_dmx_ext.h>
#ifdef CONFIG_AMLOGIC_DVB_COMPAT
#include <media/aml_demux_ext.h>
#endif
#include "dmxdev.h"
#include "demux.h"
#if IS_BUILTIN(CONFIG_AMLOGIC_MEDIA_MODULE)
#include <linux/amlogic/aml_cma.h>
#endif
static int dmabuf_manage_debug = 1;
module_param(dmabuf_manage_debug, int, 0644);
#if IS_ENABLED(CONFIG_AMLOGIC_OPTEE)
static u32 secure_heap_version = SECURE_HEAP_USER_TA_VERSION;
#else
static u32 secure_heap_version;
#endif
static u32 secure_vdec_config = 0x7C;
#define DEVICE_NAME "secmem"
#define CLASS_NAME "dmabuf_manage"
#define CONFIG_PATH "media.dmabuf_manage"
#define CONFIG_PREFIX "media"
#define dprintk(level, fmt, arg...) \
do { \
if (dmabuf_manage_debug >= (level)) \
pr_info(CLASS_NAME ": %s: " fmt, __func__, ## arg);\
} while (0)
#define pr_dbg(fmt, args ...) dprintk(6, fmt, ## args)
#define pr_error(fmt, args ...) dprintk(1, fmt, ## args)
#define pr_inf(fmt, args ...) dprintk(8, fmt, ## args)
#define pr_enter() pr_inf("enter")
#define SIZE_MB (1024 * 1024)
#if IS_ENABLED(CONFIG_AMLOGIC_OPTEE)
#define DMABUF_MANAGE_BLOCK_MIN_SIZE_KB (256 * 1024)
#define TA_SECMEM_V2_CMD_INIT 294
#define TA_SECMEM_V2_CMD_MEM_CREATE 251
#define TA_SECMEM_V2_CMD_MEM_FREE 258
#define TA_SECMEM_V2_CMD_CLOSE 266
#define TA_SECMEM_V2_SHM_SIZE 4096
#define TEE_CMD_PARAM_NUM 4
#define PARAM_ALIGN 32
#define SECMEM_TA_UUID UUID_INIT(0x2c1a33c0, 0x44cc, 0x11e5, \
0xbc, 0x3b, 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b)
#endif
struct kdmabuf_attachment {
struct sg_table sgt;
enum dma_data_direction dma_dir;
};
struct block_node {
struct list_head node;
u32 addr;
u32 size;
};
struct secure_pool_info {
struct list_head node;
struct list_head block_node;
u32 version;
u32 id_high;
u32 id_low;
u32 protect_handle;
u32 block_size;
u32 channel_register;
#if IS_ENABLED(CONFIG_AMLOGIC_OPTEE)
struct tee_ioctl_open_session_arg secmem_session;
struct tee_shm *shm_pool;
#endif
};
typedef int (*decode_info)(struct dmx_demux *demux, void *info);
static long dmabuf_manage_release_channel(u32 id_high, u32 id_low);
static int dmabuf_manage_release_dmabufheap_resource(struct secure_pool_info *release_pool);
struct dmx_filter_info {
struct list_head list;
__u32 token;
__u32 filter_fd;
struct dmx_demux *demux;
decode_info decode_info_func;
};
static struct list_head pool_list;
static struct list_head dmx_filter_list;
static int dev_no;
static struct device *dmabuf_manage_dev;
static DEFINE_MUTEX(g_secure_pool_mutex);
#if IS_ENABLED(CONFIG_AMLOGIC_OPTEE)
static struct tee_context *g_secmem_context;
enum TEE_CMD_PARAMTYPE {
TEE_CMD_PARAMTYPE_BUF,
TEE_CMD_PARAMTYPE_UINT32,
TEE_CMD_PARAMTYPE_UINT64,
TEE_CMD_PARAMTYPE_VOID,
};
struct tee_cmdparam {
u32 type;
union {
struct { /* type == tee_cmdparamType_Buf */
u32 buflen;
u32 pbuf;
} buf;
u32 u32_value; /* type == TEE_CMD_PARAMTYPE_UINT32 */
} param;
};
static int dmabuf_manage_tee_pack_u32(char *shm_data, const u32 num, u32 *poff)
{
struct tee_cmdparam p;
u32 off = 0;
if (!shm_data || !poff)
return -1;
off = *poff;
if (off > TA_SECMEM_V2_SHM_SIZE - sizeof(struct tee_cmdparam))
return -1;
p.type = TEE_CMD_PARAMTYPE_UINT32;
p.param.u32_value = num;
memcpy((void *)(shm_data + off), &p, sizeof(struct tee_cmdparam));
*poff = (off + sizeof(struct tee_cmdparam) + PARAM_ALIGN) & ~(PARAM_ALIGN - 1);
return 0;
}
static int dmabuf_manage_tee_unpack_u32(const char *shm, u32 *num, u32 *poff)
{
struct tee_cmdparam p;
u32 off = 0;
if (!shm || !poff || !num)
return -1;
off = *poff;
if (off > TA_SECMEM_V2_SHM_SIZE - sizeof(struct tee_cmdparam))
return -1;
memcpy((void *)&p, (void *)(shm + off), sizeof(struct tee_cmdparam));
if (p.type != TEE_CMD_PARAMTYPE_UINT32) {
pr_error("error param type %d", p.type);
return -1;
}
*num = p.param.u32_value;
*poff = (off + sizeof(struct tee_cmdparam) + PARAM_ALIGN) & ~(PARAM_ALIGN - 1);
return 0;
}
#endif
static int dmabuf_manage_attach(struct dma_buf *dbuf, struct dma_buf_attachment *attachment)
{
struct kdmabuf_attachment *attach;
struct dmabuf_manage_block *block = dbuf->priv;
struct sg_table *sgt;
struct page *page;
phys_addr_t phys = block->paddr;
int ret;
int sgnum = 1;
struct dmabuf_dmx_sec_es_data *es = NULL;
int len = 0;
pr_enter();
attach = (struct kdmabuf_attachment *)
kzalloc(sizeof(*attach), GFP_KERNEL);
if (!attach) {
pr_error("kzalloc failed\n");
goto error;
}
if (block->type == DMA_BUF_TYPE_DMX_ES) {
es = (struct dmabuf_dmx_sec_es_data *)block->priv;
if (es->data_end < es->data_start)
sgnum = 2;
}
sgt = &attach->sgt;
ret = sg_alloc_table(sgt, sgnum, GFP_KERNEL);
if (ret) {
pr_error("No memory for sgtable");
goto error_alloc;
}
if (sgnum == 2) {
len = PAGE_ALIGN(es->buf_end - es->data_start);
if (block->paddr != es->data_start) {
pr_error("Invalid buffer info %x %x",
block->paddr, es->data_start);
goto error_attach;
}
page = phys_to_page(phys);
sg_set_page(sgt->sgl, page, len, 0);
len = PAGE_ALIGN(es->data_end - es->buf_start);
page = phys_to_page(es->buf_start);
sg_set_page(sg_next(sgt->sgl), page, len, 0);
} else {
page = phys_to_page(phys);
sg_set_page(sgt->sgl, page, PAGE_ALIGN(block->size), 0);
}
attach->dma_dir = DMA_NONE;
attachment->priv = attach;
return 0;
error_attach:
sg_free_table(sgt);
error_alloc:
kfree(attach);
error:
return 0;
}
static void dmabuf_manage_detach(struct dma_buf *dbuf,
struct dma_buf_attachment *attachment)
{
struct kdmabuf_attachment *attach = attachment->priv;
struct sg_table *sgt;
pr_enter();
if (!attach)
return;
sgt = &attach->sgt;
sg_free_table(sgt);
kfree(attach);
attachment->priv = NULL;
}
static struct sg_table *dmabuf_manage_map_dma_buf(struct dma_buf_attachment *attachment,
enum dma_data_direction dma_dir)
{
struct kdmabuf_attachment *attach = attachment->priv;
struct dmabuf_manage_block *block = attachment->dmabuf->priv;
#if CONFIG_AMLOGIC_KERNEL_VERSION <= 14515
struct mutex *lock = &attachment->dmabuf->lock;
#endif
struct sg_table *sgt;
pr_enter();
#if CONFIG_AMLOGIC_KERNEL_VERSION <= 14515
mutex_lock(lock);
#endif
sgt = &attach->sgt;
if (attach->dma_dir == dma_dir) {
#if CONFIG_AMLOGIC_KERNEL_VERSION <= 14515
mutex_unlock(lock);
#endif
return sgt;
}
sgt->sgl->dma_address = block->paddr;
#ifdef CONFIG_NEED_SG_DMA_LENGTH
sgt->sgl->dma_length = PAGE_ALIGN(block->size);
#else
sgt->sgl->length = PAGE_ALIGN(block->size);
#endif
pr_dbg("nents %d, %x, %d, %d\n", sgt->nents, block->paddr,
sg_dma_len(sgt->sgl), block->size);
attach->dma_dir = dma_dir;
#if CONFIG_AMLOGIC_KERNEL_VERSION <= 14515
mutex_unlock(lock);
#endif
return sgt;
}
static void dmabuf_manage_unmap_dma_buf(struct dma_buf_attachment *attachment,
struct sg_table *sgt,
enum dma_data_direction dma_dir)
{
pr_enter();
}
static void dmabuf_manage_buf_release(struct dma_buf *dbuf)
{
struct dmabuf_manage_block *block = NULL;
struct dmabuf_dmx_sec_es_data *es = NULL;
struct dmx_filter_info *node = NULL;
struct secure_vdec_channel *channel = NULL;
struct list_head *pos = NULL, *tmp = NULL;
int found = 0;
struct decoder_mem_info rp_info;
pr_enter();
block = (struct dmabuf_manage_block *)dbuf->priv;
if (block && block->priv && block->type == DMA_BUF_TYPE_DMX_ES) {
es = (struct dmabuf_dmx_sec_es_data *)block->priv;
list_for_each_safe(pos, tmp, &dmx_filter_list) {
node = list_entry(pos, struct dmx_filter_info, list);
if (node && node->token == es->token) {
found = 1;
break;
}
}
if (found) {
if (es->buf_rp == 0)
es->buf_rp = es->data_end;
if (es->buf_rp >= es->buf_start && es->buf_rp <= es->buf_end &&
node->demux && node->decode_info_func) {
rp_info.rp_phy = es->buf_rp;
node->decode_info_func(node->demux, &rp_info);
}
}
} else if (block && block->type == DMA_BUF_TYPE_DMABUF) {
if (block->flags & DMABUF_ALLOC_FROM_CMA)
codec_mm_free_for_dma("dmabuf", block->paddr);
} else if (block && block->priv &&
block->type == DMA_BUF_TYPE_SECURE_VDEC) {
channel = (struct secure_vdec_channel *)block->priv;
dmabuf_manage_release_channel(channel->id_high, channel->id_low);
}
if (block) {
pr_dbg("dma release handle:%x\n", block->handle);
kfree(block->priv);
kfree(block);
}
}
static int dmabuf_manage_mmap(struct dma_buf *dbuf, struct vm_area_struct *vma)
{
struct dmabuf_manage_block *block;
struct dmabuf_dmx_sec_es_data *es;
unsigned long addr = vma->vm_start;
int len = 0;
int ret = -EFAULT;
pr_enter();
block = (struct dmabuf_manage_block *)dbuf->priv;
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
if (block->type == DMA_BUF_TYPE_DMX_ES) {
es = (struct dmabuf_dmx_sec_es_data *)block->priv;
if (es->data_end >= es->data_start) {
len = PAGE_ALIGN(es->data_end - es->data_start);
if (block->paddr != es->data_start) {
pr_error("Invalid buffer info %x %x",
block->paddr, es->data_start);
goto error;
}
ret = remap_pfn_range(vma, addr,
page_to_pfn(phys_to_page(block->paddr)),
len, vma->vm_page_prot);
} else {
len = PAGE_ALIGN(es->buf_end - es->data_start);
if (block->paddr != es->data_start) {
pr_error("Invalid buffer info %x %x",
block->paddr, es->data_start);
goto error;
}
ret = remap_pfn_range(vma, addr,
page_to_pfn(phys_to_page(block->paddr)),
len, vma->vm_page_prot);
if (ret) {
pr_error("remap failed %d", ret);
goto error;
}
addr += len;
if (addr >= vma->vm_end)
return ret;
len = PAGE_ALIGN(es->data_end - es->buf_start);
ret = remap_pfn_range(vma, addr,
page_to_pfn(phys_to_page(es->buf_start)),
len, vma->vm_page_prot);
}
} else {
ret = remap_pfn_range(vma, vma->vm_start, page_to_pfn(phys_to_page(block->paddr)),
block->size, vma->vm_page_prot);
}
error:
return ret;
}
static struct dma_buf_ops dmabuf_manage_ops = {
.attach = dmabuf_manage_attach,
.detach = dmabuf_manage_detach,
.map_dma_buf = dmabuf_manage_map_dma_buf,
.unmap_dma_buf = dmabuf_manage_unmap_dma_buf,
.release = dmabuf_manage_buf_release,
.mmap = dmabuf_manage_mmap
};
bool dmabuf_is_esbuf(struct dma_buf *dmabuf)
{
return dmabuf->ops == &dmabuf_manage_ops;
}
EXPORT_SYMBOL(dmabuf_is_esbuf);
static struct dma_buf *get_dmabuf(struct dmabuf_manage_block *block,
unsigned long flags)
{
struct dma_buf *dbuf;
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
pr_dbg("export handle:%x, paddr:%x, size:%x\n",
block->handle, block->paddr, block->size);
exp_info.ops = &dmabuf_manage_ops;
exp_info.size = block->size;
exp_info.flags = flags;
exp_info.priv = (void *)block;
exp_info.exp_name = "dmabuf_manage";
dbuf = dma_buf_export(&exp_info);
if (IS_ERR(dbuf))
return NULL;
return dbuf;
}
static long dmabuf_manage_export(unsigned long args)
{
int ret;
struct dmabuf_manage_block *block;
struct dma_buf *dbuf;
int fd;
int fd_flags = O_CLOEXEC;
pr_enter();
block = (struct dmabuf_manage_block *)
kzalloc(sizeof(*block), GFP_KERNEL);
if (!block) {
pr_error("kmalloc failed\n");
goto error_alloc_object;
}
ret = copy_from_user((void *)block, (void __user *)args,
sizeof(struct secmem_block));
if (ret) {
pr_error("copy_from_user failed\n");
goto error_copy;
}
block->type = DMA_BUF_TYPE_SECMEM;
dbuf = get_dmabuf(block, fd_flags);
if (!dbuf) {
pr_error("get_dmabuf failed\n");
goto error_export;
}
fd = dma_buf_fd(dbuf, fd_flags);
if (fd < 0) {
pr_error("dma_buf_fd failed, ret:%d, dbuf:%px, file:%px\n",
fd, dbuf, dbuf->file);
goto error_fd;
}
return fd;
error_fd:
dma_buf_put(dbuf);
return fd;
error_export:
error_copy:
kfree(block);
error_alloc_object:
return -EFAULT;
}
static long dmabuf_manage_get_handle(unsigned long args)
{
int ret;
long res = -EFAULT;
int fd;
struct dma_buf *dbuf;
struct dmabuf_manage_block *block;
pr_enter();
ret = copy_from_user((void *)&fd, (void __user *)args, sizeof(fd));
if (ret) {
pr_error("copy_from_user failed\n");
goto error_copy;
}
dbuf = dma_buf_get(fd);
if (IS_ERR(dbuf)) {
pr_error("dma_buf_get failed\n");
goto error_get;
}
block = dbuf->priv;
res = (long)(block->handle & (0xffffffff));
dma_buf_put(dbuf);
error_get:
error_copy:
return res;
}
static long dmabuf_manage_get_phyaddr(unsigned long args)
{
int ret;
long res = -EFAULT;
int fd;
struct dma_buf *dbuf;
struct dmabuf_manage_block *block;
pr_enter();
ret = copy_from_user((void *)&fd, (void __user *)args, sizeof(fd));
if (ret) {
pr_error("copy_from_user failed\n");
goto error_copy;
}
dbuf = dma_buf_get(fd);
if (IS_ERR(dbuf)) {
pr_error("dma_buf_get failed\n");
goto error_get;
}
block = dbuf->priv;
res = (long)(block->paddr & (0xffffffff));
dma_buf_put(dbuf);
error_get:
error_copy:
return res;
}
static long dmabuf_manage_import(unsigned long args)
{
int ret;
long res = -EFAULT;
int fd;
struct dma_buf *dbuf;
struct dma_buf_attachment *attach;
struct sg_table *sgt;
dma_addr_t paddr;
pr_enter();
ret = copy_from_user((void *)&fd, (void __user *)args, sizeof(fd));
if (ret) {
pr_error("copy_from_user failed\n");
goto error_copy;
}
dbuf = dma_buf_get(fd);
if (IS_ERR(dbuf)) {
pr_error("dma_buf_get failed\n");
goto error_get;
}
attach = dma_buf_attach(dbuf, dmabuf_manage_dev);
if (IS_ERR(attach)) {
pr_error("dma_buf_attach failed\n");
goto error_attach;
}
sgt = dma_buf_map_attachment(attach, DMA_FROM_DEVICE);
if (IS_ERR(sgt)) {
pr_error("dma_buf_map_attachment failed\n");
goto error_map;
}
paddr = sg_dma_address(sgt->sgl);
res = (long)(paddr & (0xffffffff));
dma_buf_unmap_attachment(attach, sgt, DMA_FROM_DEVICE);
error_map:
dma_buf_detach(dbuf, attach);
error_attach:
dma_buf_put(dbuf);
error_get:
error_copy:
return res;
}
static long dmabuf_manage_export_dmabuf(unsigned long args)
{
long res = -EFAULT;
struct dmabuf_manage_buffer info;
struct dmabuf_manage_block *block;
struct dmabuf_dmx_sec_es_data *dmxes;
struct dmabuf_videodec_es_data *vdecdata;
struct dma_buf *dbuf;
int fd = -1;
int fd_flags = O_CLOEXEC;
pr_enter();
res = copy_from_user((void *)&info, (void __user *)args, sizeof(info));
if (res) {
pr_error("copy_from_user failed\n");
goto error_copy;
}
if (info.type == DMA_BUF_TYPE_INVALID) {
pr_error("unknown dma buf type\n");
goto error_copy;
}
block = kzalloc(sizeof(*block), GFP_KERNEL);
if (!block) {
pr_error("kmalloc failed\n");
goto error_copy;
}
switch (info.type) {
case DMA_BUF_TYPE_SECMEM:
fd_flags = O_CLOEXEC;
break;
case DMA_BUF_TYPE_DMX_ES:
fd_flags = O_CLOEXEC;
dmxes = kzalloc(sizeof(*dmxes), GFP_KERNEL);
if (!dmxes) {
pr_error("kmalloc failed\n");
goto error_alloc_object;
}
memcpy(dmxes, &info.buffer.dmxes, sizeof(*dmxes));
block->priv = dmxes;
break;
case DMA_BUF_TYPE_DMABUF:
fd_flags = O_RDWR | O_CLOEXEC;
break;
case DMA_BUF_TYPE_VIDEODEC_ES:
fd_flags = O_RDWR | O_CLOEXEC;
vdecdata = kzalloc(sizeof(*vdecdata), GFP_KERNEL);
if (!vdecdata) {
pr_error("kmalloc failed\n");
goto error_alloc_object;
}
memcpy(vdecdata, &info.buffer.vdecdata, sizeof(*vdecdata));
block->priv = vdecdata;
break;
default:
block->priv = NULL;
break;
}
block->paddr = info.paddr;
block->size = PAGE_ALIGN(info.size);
block->handle = info.handle;
block->type = info.type;
dbuf = get_dmabuf(block, fd_flags);
if (!dbuf) {
pr_error("get_dmabuf failed\n");
goto error_alloc_object;
}
fd = dma_buf_fd(dbuf, fd_flags);
if (fd < 0) {
pr_error("dma_buf_fd failed, ret:%d, dbuf:%px, file:%px\n",
fd, dbuf, dbuf->file);
goto error_fd;
}
return fd;
error_fd:
dma_buf_put(dbuf);
return fd;
error_alloc_object:
kfree(block->priv);
kfree(block);
error_copy:
return res;
}
static long dmabuf_manage_get_dmabufinfo(unsigned long args)
{
struct dmabuf_manage_buffer info;
struct dmabuf_manage_block *block;
struct dma_buf *dbuf;
pr_enter();
if (copy_from_user((void *)&info, (void __user *)args, sizeof(info))) {
pr_error("copy_from_user failed\n");
goto error;
}
if (info.type == DMA_BUF_TYPE_INVALID)
goto error;
dbuf = dma_buf_get(info.fd);
if (IS_ERR(dbuf))
goto error;
if (dbuf->priv && dbuf->ops == &dmabuf_manage_ops) {
block = dbuf->priv;
if (block->type != info.type)
goto error;
info.paddr = block->paddr;
info.size = block->size;
info.handle = block->handle;
switch (info.type) {
case DMA_BUF_TYPE_DMX_ES:
memcpy(&info.buffer.dmxes, block->priv,
sizeof(struct dmabuf_dmx_sec_es_data));
break;
case DMA_BUF_TYPE_VIDEODEC_ES:
memcpy(&info.buffer.vdecdata, block->priv,
sizeof(struct dmabuf_videodec_es_data));
break;
default:
break;
}
if (copy_to_user((void __user *)args, &info, sizeof(info))) {
pr_error("error copy to use space");
goto error_fd;
}
}
dma_buf_put(dbuf);
return 0;
error_fd:
dma_buf_put(dbuf);
error:
return -EFAULT;
}
static long dmabuf_manage_set_filterfd(unsigned long args)
{
struct filter_info info;
struct dmx_filter_info *node = NULL;
struct list_head *pos, *tmp;
int found = 0;
struct dmx_demux *demux = NULL;
decode_info decode_info_func = NULL;
#ifdef CONFIG_AMLOGIC_DVB_COMPAT
struct fd f;
struct dmxdev_filter *dmxdevfilter = NULL;
struct dmxdev *dmxdev = NULL;
struct dmx_demux_ext *demux_ext = NULL;
#endif
pr_enter();
if (copy_from_user((void *)&info, (void __user *)args, sizeof(info))) {
pr_error("copy_from_user failed\n");
goto error;
}
if (list_empty(&dmx_filter_list)) {
if (info.release) {
pr_error("No filter info setting\n");
goto error;
} else {
node = kzalloc(sizeof(*node), GFP_KERNEL);
if (!node)
goto error;
list_add_tail(&node->list, &dmx_filter_list);
}
} else {
list_for_each_safe(pos, tmp, &dmx_filter_list) {
node = list_entry(pos, struct dmx_filter_info, list);
if (node->token == info.token && node->filter_fd == info.filter_fd) {
found = 1;
break;
}
}
if (info.release) {
if (!found) {
pr_error("No filter info setting\n");
goto error;
} else {
list_del(&node->list);
kfree(node);
node = NULL;
}
} else {
if (!found) {
node = kzalloc(sizeof(*node), GFP_KERNEL);
if (!node)
goto error;
list_add_tail(&node->list, &dmx_filter_list);
}
}
}
if (node) {
node->token = info.token;
node->filter_fd = info.filter_fd;
#ifdef CONFIG_AMLOGIC_DVB_COMPAT
f = fdget(node->filter_fd);
if (f.file && f.file->private_data) {
dmxdevfilter = f.file->private_data;
if (dmxdevfilter)
dmxdev = dmxdevfilter->dev;
}
if (dmxdev && dmxdev->demux) {
demux = dmxdev->demux;
demux_ext = container_of(demux, struct dmx_demux_ext, dmx);
decode_info_func = demux_ext->decode_info;
} else {
pr_error("Invalid filter fd\n");
}
fdput(f);
#endif
node->demux = demux;
node->decode_info_func = decode_info_func;
}
return 0;
error:
return -EFAULT;
}
static int dmabuf_manage_alloc_dmabuf(unsigned long args)
{
int res = -EFAULT;
struct dmabuf_manage_buffer info;
int fd_flags = O_RDWR | O_CLOEXEC;
struct dmabuf_manage_block *block;
struct dma_buf *dbuf;
pr_enter();
res = copy_from_user((void *)&info, (void __user *)args, sizeof(info));
if (res)
goto error_copy;
if (info.size <= 0 || info.size % 4096 != 0) {
pr_error("Invalid size isn't 4K align %d", info.size);
goto error_copy;
}
block = kzalloc(sizeof(*block), GFP_KERNEL);
if (!block) {
pr_error("kmalloc failed\n");
goto error_copy;
}
block->paddr = codec_mm_alloc_for_dma("dmabuf", info.size / PAGE_SIZE,
PAGE_SHIFT, CODEC_MM_FLAGS_DMA);
if (block->paddr <= 0)
goto error_alloc_object;
block->size = PAGE_ALIGN(info.size);
block->handle = info.handle;
block->type = info.type;
block->flags |= DMABUF_ALLOC_FROM_CMA;
dbuf = get_dmabuf(block, fd_flags);
if (!dbuf) {
pr_error("get_dmabuf failed\n");
goto error_alloc;
}
res = dma_buf_fd(dbuf, fd_flags);
if (res < 0) {
pr_error("dma_buf_fd failed\n");
goto error_fd;
}
return res;
error_fd:
dma_buf_put(dbuf);
error_alloc:
codec_mm_free_for_dma("dmabuf", block->paddr);
error_alloc_object:
kfree(block);
error_copy:
return -EFAULT;
}
unsigned int dmabuf_manage_get_type(struct dma_buf *dbuf)
{
int ret = DMA_BUF_TYPE_INVALID;
struct dmabuf_manage_block *block;
if (!dbuf) {
pr_dbg("acquire dma_buf failed");
goto error;
}
if (dbuf->priv && dbuf->ops == &dmabuf_manage_ops) {
block = (struct dmabuf_manage_block *)dbuf->priv;
if (block)
ret = block->type;
}
error:
return ret;
}
EXPORT_SYMBOL(dmabuf_manage_get_type);
void *dmabuf_manage_get_info(struct dma_buf *dbuf, unsigned int type)
{
void *buf = NULL;
struct dmabuf_manage_block *block;
if (type == DMA_BUF_TYPE_INVALID)
goto error;
if (!dbuf) {
pr_dbg("acquire dma_buf failed");
goto error;
}
if (dbuf->priv && dbuf->ops == &dmabuf_manage_ops) {
block = (struct dmabuf_manage_block *)dbuf->priv;
switch (block->type) {
case DMA_BUF_TYPE_SECMEM:
buf = block;
break;
case DMA_BUF_TYPE_DMX_ES:
buf = block->priv;
break;
case DMA_BUF_TYPE_DMABUF:
buf = block;
break;
case DMA_BUF_TYPE_VIDEODEC_ES:
buf = block->priv;
break;
default:
break;
}
}
error:
return buf;
}
EXPORT_SYMBOL(dmabuf_manage_get_info);
static struct secure_pool_info *dmabuf_manage_get_secure_vdec_pool(u32 id_high,
u32 id_low)
{
struct list_head *pos = NULL;
struct list_head *tmp = NULL;
struct secure_pool_info *pool = NULL;
if (!list_empty(&pool_list)) {
list_for_each_safe(pos, tmp, &pool_list) {
pool = list_entry(pos, struct secure_pool_info, node);
if (pool && pool->id_high == id_high && pool->id_low == id_low)
return pool;
}
}
return NULL;
}
#if IS_ENABLED(CONFIG_AMLOGIC_OPTEE)
static int dmabuf_manage_secmem_ctx_match(struct tee_ioctl_version_data *ver,
const void *data)
{
return (ver && ver->impl_id == TEE_IMPL_ID_OPTEE);
}
static int dmabuf_manage_secure_v2_init(struct secure_pool_info *pool, u32 flags)
{
int res = 0;
u32 in_len = 0;
u32 config = 0;
u32 tvp_flag = flags & 0x0F;
u32 decoder_flag = (flags >> 4) & 0x0F;
u32 vd_index = (flags >> 9) & 0x0F;
char *shm_data = NULL;
struct tee_param param[TEE_CMD_PARAM_NUM] = { 0 };
struct tee_ioctl_invoke_arg inv_arg = { 0 };
if (!pool || !g_secmem_context)
return -1;
if (!pool->shm_pool)
return -1;
config = secure_vdec_config;
if ((decoder_flag & 0x3) == 0x3)
config |= 0x400000;
if ((decoder_flag & 0x8) == 0x8)
config |= 0x2;
if (tvp_flag == 0x2)
config |= 0x100;
config |= (vd_index << 11);
config |= 0x10000;
shm_data = tee_shm_get_va(pool->shm_pool, 0);
if (IS_ERR(shm_data)) {
pr_error("%s tee_shm_get_va failed\n", __func__);
res = -EBUSY;
goto error;
}
res = dmabuf_manage_tee_pack_u32(shm_data, TA_SECMEM_V2_CMD_INIT, &in_len);
if (res) {
pr_error("%s pass tee parameter failed\n", __func__);
res = -EFAULT;
goto error;
}
res = dmabuf_manage_tee_pack_u32(shm_data, 1, &in_len);
if (res) {
pr_error("%s pass tee parameter failed\n", __func__);
res = -EFAULT;
goto error;
}
res = dmabuf_manage_tee_pack_u32(shm_data, config, &in_len);
if (res) {
pr_error("%s pass tee parameter failed\n", __func__);
res = -EFAULT;
goto error;
}
res = dmabuf_manage_tee_pack_u32(shm_data, 0, &in_len);
if (res) {
pr_error("%s pass tee parameter failed\n", __func__);
res = -EFAULT;
goto error;
}
res = dmabuf_manage_tee_pack_u32(shm_data, 0, &in_len);
if (res) {
pr_error("%s pass tee parameter failed\n", __func__);
res = -EFAULT;
goto error;
}
res = dmabuf_manage_tee_pack_u32(shm_data, pool->version, &in_len);
if (res) {
pr_error("%s pass tee parameter failed\n", __func__);
res = -EFAULT;
goto error;
}
res = dmabuf_manage_tee_pack_u32(shm_data, pool->id_high, &in_len);
if (res) {
pr_error("%s pass tee parameter failed\n", __func__);
res = -EFAULT;
goto error;
}
res = dmabuf_manage_tee_pack_u32(shm_data, pool->id_low, &in_len);
if (res) {
pr_error("%s pass tee parameter failed\n", __func__);
res = -EFAULT;
goto error;
}
param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT;
param[0].u.memref.shm = pool->shm_pool;
param[0].u.memref.size = in_len;
param[0].u.memref.shm_offs = 0;
param[3].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;
inv_arg.func = TA_SECMEM_V2_CMD_INIT;
inv_arg.session = pool->secmem_session.session;
inv_arg.num_params = TEE_CMD_PARAM_NUM;
res = tee_client_invoke_func(g_secmem_context, &inv_arg, param);
if (res < 0 || inv_arg.ret != TEEC_SUCCESS) {
pr_error("%s invoke func failed, res %d, res 0x%x,origin = 0x%x\n",
__func__, res, inv_arg.ret, inv_arg.ret_origin);
res = inv_arg.ret;
}
error:
return res;
}
static int dmabuf_manage_secmem_pool_init(u32 id_high, u32 id_low, u32 block_size, u32 version)
{
int res = 1;
struct secure_pool_info *pool = NULL;
uuid_t uuid = SECMEM_TA_UUID;
unsigned int tvp_set = 0;
unsigned int codec_flags = 0;
unsigned int vd_index = 1;
if (version != SECURE_HEAP_USER_TA_VERSION)
goto error;
pool = kzalloc(sizeof(*pool), GFP_KERNEL);
if (!pool)
goto error;
pool->version = version;
pool->id_high = id_high;
pool->id_low = id_low;
pool->block_size = block_size;
if (block_size <= DMABUF_MANAGE_BLOCK_MIN_SIZE_KB) {
codec_flags = 0x3;
codec_flags |= 0x8;
} else if (block_size <= 2 * SIZE_MB) {
tvp_set = 1;
} else {
tvp_set = 2;
}
if (!g_secmem_context) {
g_secmem_context = tee_client_open_context(NULL,
dmabuf_manage_secmem_ctx_match, NULL, NULL);
if (IS_ERR(g_secmem_context)) {
pr_error("%s open context failed\n", __func__);
goto error_alloc;
}
}
memcpy(pool->secmem_session.uuid, uuid.b, TEE_IOCTL_UUID_LEN);
pool->secmem_session.clnt_login = TEE_IOCTL_LOGIN_PUBLIC;
pool->secmem_session.num_params = 0;
res = tee_client_open_session(g_secmem_context, &pool->secmem_session, NULL);
if (res < 0 || pool->secmem_session.ret != TEEC_SUCCESS) {
pr_error("%s open session ret %d, res 0x%x, origin 0x%x\n",
__func__, res, pool->secmem_session.ret, pool->secmem_session.ret_origin);
res = pool->secmem_session.ret;
goto error_alloc;
}
pool->shm_pool = tee_shm_alloc_kernel_buf(g_secmem_context,
TA_SECMEM_V2_SHM_SIZE);
if (IS_ERR(pool->shm_pool)) {
pr_error("%s tee_shm_alloc failed\n", __func__);
res = -ENOMEM;
goto error_alloc_shm;
}
res = dmabuf_manage_secure_v2_init(pool,
tvp_set | (codec_flags << 4) | (vd_index << 9));
if (res) {
pr_error("%s secure v2 init failed\n", __func__);
goto error_open_session;
}
INIT_LIST_HEAD(&pool->block_node);
list_add_tail(&pool->node, &pool_list);
return 0;
error_open_session:
tee_shm_free(pool->shm_pool);
error_alloc_shm:
tee_client_close_session(g_secmem_context, pool->secmem_session.session);
error_alloc:
kfree(pool);
error:
return res;
}
static void dmabuf_manage_destroy_secmem_pool(struct secure_pool_info *pool)
{
int res = 0;
u32 in_len = 0;
char *shm_data = NULL;
struct tee_param param[TEE_CMD_PARAM_NUM] = { 0 };
struct tee_ioctl_invoke_arg inv_arg = { 0 };
if (!pool)
return;
if (list_empty(&pool->block_node)) {
if (pool->shm_pool) {
shm_data = tee_shm_get_va(pool->shm_pool, 0);
if (IS_ERR(shm_data)) {
pr_error("%s tee_shm_get_va failed\n", __func__);
return;
}
res = dmabuf_manage_tee_pack_u32(shm_data, TA_SECMEM_V2_CMD_CLOSE, &in_len);
if (res) {
pr_error("%s pass tee parameter failed\n", __func__);
return;
}
param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT;
param[0].u.memref.shm = pool->shm_pool;
param[0].u.memref.size = in_len;
param[0].u.memref.shm_offs = 0;
param[3].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;
inv_arg.func = TA_SECMEM_V2_CMD_CLOSE;
inv_arg.session = pool->secmem_session.session;
inv_arg.num_params = TEE_CMD_PARAM_NUM;
res = tee_client_invoke_func(g_secmem_context, &inv_arg, param);
if (res < 0 || inv_arg.ret != TEEC_SUCCESS) {
pr_error("%s invoke func failed, res %d, res 0x%x, origin = 0x%x\n",
__func__, res, inv_arg.ret, inv_arg.ret_origin);
res = inv_arg.ret;
return;
}
tee_shm_free(pool->shm_pool);
res = tee_client_close_session(g_secmem_context,
pool->secmem_session.session);
if (res)
pr_error("close secmem session error 0x%x", res);
}
list_del(&pool->node);
kfree(pool);
}
}
#endif
int dmabuf_manage_secure_pool_create(u32 id_high, u32 id_low, u32 block_size,
u32 version)
{
int res = 0;
struct secure_pool_info *pool = NULL;
#if IS_ENABLED(CONFIG_AMLOGIC_OPTEE)
mutex_lock(&g_secure_pool_mutex);
pool = dmabuf_manage_get_secure_vdec_pool(id_high, id_low);
if (!pool)
res = dmabuf_manage_secmem_pool_init(id_high, id_low,
block_size, version);
mutex_unlock(&g_secure_pool_mutex);
#else
res = -1;
#endif
return res;
}
static long dmabuf_manage_register_channel(unsigned long args)
{
struct secure_vdec_channel *channel = NULL;
struct secure_pool_info *pool = NULL;
int ret = 0;
int fd = -1;
int fd_flags = O_CLOEXEC;
struct dmabuf_manage_block *block = NULL;
struct dma_buf *dbuf = NULL;
mutex_lock(&g_secure_pool_mutex);
pr_enter();
channel = (struct secure_vdec_channel *)
kzalloc(sizeof(*channel), GFP_KERNEL);
if (!channel) {
pr_error("kmalloc channel failed\n");
goto error_alloc_channel;
}
ret = copy_from_user((void *)channel, (void __user *)args, sizeof(*channel));
if (ret) {
pr_error("copy channel information failed\n");
goto error_copy;
}
pool = dmabuf_manage_get_secure_vdec_pool(channel->id_high, channel->id_low);
if (pool) {
pool->channel_register = 1;
block = (struct dmabuf_manage_block *)
kzalloc(sizeof(*block), GFP_KERNEL);
if (!block) {
pr_error("kmalloc failed\n");
goto error_alloc_object;
}
block->handle = pool->protect_handle;
block->size = pool->block_size;
block->type = DMA_BUF_TYPE_SECURE_VDEC;
block->priv = (void *)channel;
dbuf = get_dmabuf(block, fd_flags);
if (!dbuf) {
pr_error("get_dmabuf failed\n");
goto error_export;
}
fd = dma_buf_fd(dbuf, fd_flags);
if (fd < 0) {
pr_error("dma_buf_fd failed\n");
goto error_fd;
}
} else {
goto error_copy;
}
mutex_unlock(&g_secure_pool_mutex);
return fd;
error_fd:
dma_buf_put(dbuf);
error_export:
kfree(block);
error_alloc_object:
error_copy:
kfree(channel);
error_alloc_channel:
mutex_unlock(&g_secure_pool_mutex);
return -EFAULT;
}
static long dmabuf_manage_release_channel(u32 id_high, u32 id_low)
{
struct secure_pool_info *pool = NULL;
pr_enter();
mutex_lock(&g_secure_pool_mutex);
pool = dmabuf_manage_get_secure_vdec_pool(id_high, id_low);
if (pool) {
pool->channel_register = 0;
dmabuf_manage_release_dmabufheap_resource(pool);
}
mutex_unlock(&g_secure_pool_mutex);
return 0;
}
static void dmabuf_manage_dump_secure_pool(struct secure_pool_info *pool)
{
char buf[1024] = { 0 };
u32 size = 1024;
u32 s = 0;
u32 tsize = 0;
char *pbuf = buf;
u32 block_count = 0;
struct list_head *pos = NULL;
struct list_head *tmp = NULL;
struct block_node *block = NULL;
if (pool) {
s = snprintf(pbuf, size - tsize,
"Pool Ver %d %d Id %d %d Block %x %x\n",
pool->version, pool->channel_register,
pool->id_high, pool->id_low, pool->protect_handle,
pool->block_size);
tsize += s;
pbuf += s;
if (!list_empty(&pool->block_node)) {
list_for_each_safe(pos, tmp, &pool->block_node) {
block = list_entry(pos, struct block_node, node);
if (block) {
s = snprintf(pbuf, size - tsize,
"Block %x addr %x size %x\n", block_count,
block->addr, block->size);
tsize += s;
pbuf += s;
block_count++;
}
}
}
pr_error("%s", buf);
}
}
#if IS_ENABLED(CONFIG_AMLOGIC_OPTEE)
static int dmabuf_manage_secmem_block_alloc(struct secure_pool_info *pool, u32 size, u32 *handle)
{
int res = 0;
u32 in_len = 0;
u32 out_off = 0;
char *shm_data = NULL;
struct tee_param param[TEE_CMD_PARAM_NUM] = { 0 };
struct tee_ioctl_invoke_arg inv_arg = { 0 };
if (!pool || !g_secmem_context || !handle)
return -1;
if (!pool->shm_pool)
return -1;
shm_data = tee_shm_get_va(pool->shm_pool, 0);
if (IS_ERR(shm_data)) {
pr_error("%s tee_shm_get_va failed\n", __func__);
res = -EBUSY;
goto error;
}
res = dmabuf_manage_tee_pack_u32(shm_data, TA_SECMEM_V2_CMD_MEM_CREATE, &in_len);
if (res) {
pr_error("%s pass tee parameter failed\n", __func__);
res = -EFAULT;
goto error;
}
param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT;
param[0].u.memref.shm = pool->shm_pool;
param[0].u.memref.size = in_len;
param[0].u.memref.shm_offs = 0;
param[3].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;
inv_arg.func = TA_SECMEM_V2_CMD_MEM_CREATE;
inv_arg.session = pool->secmem_session.session;
inv_arg.num_params = TEE_CMD_PARAM_NUM;
res = tee_client_invoke_func(g_secmem_context, &inv_arg, param);
if (res < 0 || inv_arg.ret != TEEC_SUCCESS) {
pr_error("%s invoke func failed, res %d, res 0x%x,origin = 0x%x\n",
__func__, res, inv_arg.ret, inv_arg.ret_origin);
res = inv_arg.ret;
goto error;
}
res = dmabuf_manage_tee_unpack_u32(shm_data, handle, &out_off);
error:
return res;
}
static int dmabuf_manage_secmem_block_free(struct secure_pool_info *pool, u32 handle)
{
int res = 0;
u32 in_len = 0;
char *shm_data = NULL;
struct tee_param param[TEE_CMD_PARAM_NUM] = { 0 };
struct tee_ioctl_invoke_arg inv_arg = { 0 };
if (!pool || !g_secmem_context)
return -1;
if (!pool->shm_pool)
return -1;
shm_data = tee_shm_get_va(pool->shm_pool, 0);
if (IS_ERR(shm_data)) {
pr_error("%s tee_shm_get_va failed\n", __func__);
res = -EBUSY;
goto error;
}
res = dmabuf_manage_tee_pack_u32(shm_data, TA_SECMEM_V2_CMD_MEM_FREE, &in_len);
if (res) {
pr_error("%s pass tee parameter failed\n", __func__);
res = -EFAULT;
goto error;
}
res = dmabuf_manage_tee_pack_u32(shm_data, handle, &in_len);
if (res) {
pr_error("%s pass tee parameter failed\n", __func__);
res = -EFAULT;
goto error;
}
param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT;
param[0].u.memref.shm = pool->shm_pool;
param[0].u.memref.size = in_len;
param[0].u.memref.shm_offs = 0;
param[3].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;
inv_arg.func = TA_SECMEM_V2_CMD_MEM_FREE;
inv_arg.session = pool->secmem_session.session;
inv_arg.num_params = TEE_CMD_PARAM_NUM;
res = tee_client_invoke_func(g_secmem_context, &inv_arg, param);
if (res < 0 || inv_arg.ret != TEEC_SUCCESS) {
pr_error("%s invoke func failed, res %d, res 0x%x, origin = 0x%x\n",
__func__, res, inv_arg.ret, inv_arg.ret_origin);
res = inv_arg.ret;
}
error:
return res;
}
#endif
static int dmabuf_manage_release_dmabufheap_resource(struct secure_pool_info *pool)
{
#if IS_ENABLED(CONFIG_AMLOGIC_OPTEE)
struct block_node *block = NULL;
struct list_head *pos = NULL, *tmp = NULL;
if (pool) {
if (!list_empty(&pool->block_node)) {
list_for_each_safe(pos, tmp, &pool->block_node) {
block = list_entry(pos, struct block_node, node);
if (block && block->addr) {
dmabuf_manage_secmem_block_free(pool, block->addr);
list_del(&block->node);
kfree(block);
}
}
}
dmabuf_manage_destroy_secmem_pool(pool);
}
#endif
return 0;
}
phys_addr_t dmabuf_manage_secure_block_alloc(u32 id_high, u32 id_low, u32 size,
u32 version)
{
struct secure_pool_info *pool = NULL;
phys_addr_t addr = 0;
struct block_node *block = NULL;
mutex_lock(&g_secure_pool_mutex);
pool = dmabuf_manage_get_secure_vdec_pool(id_high, id_low);
if (pool) {
#if IS_ENABLED(CONFIG_AMLOGIC_OPTEE)
dmabuf_manage_secmem_block_alloc(pool, size, (u32 *)&addr);
#endif
if (addr) {
block = kzalloc(sizeof(*block), GFP_KERNEL);
if (block) {
block->addr = addr;
block->size = size;
list_add_tail(&block->node, &pool->block_node);
} else {
pr_error("No Memory for secure block\n");
}
} else {
dmabuf_manage_dump_secure_pool(pool);
}
} else {
pr_error("Can't found pool for %d %d\n", id_high, id_low);
}
mutex_unlock(&g_secure_pool_mutex);
return addr;
}
int dmabuf_manage_secure_block_free(u32 id_high, u32 id_low,
phys_addr_t addr, u32 size, u32 version)
{
struct secure_pool_info *pool = NULL;
struct list_head *pos = NULL;
struct list_head *tmp = NULL;
struct block_node *block = NULL;
mutex_lock(&g_secure_pool_mutex);
pool = dmabuf_manage_get_secure_vdec_pool(id_high, id_low);
if (pool) {
if (addr && size > 0) {
#if IS_ENABLED(CONFIG_AMLOGIC_OPTEE)
if (version == SECURE_HEAP_USER_TA_VERSION)
dmabuf_manage_secmem_block_free(pool, addr);
#endif
if (!list_empty(&pool->block_node)) {
list_for_each_safe(pos, tmp, &pool->block_node) {
block = list_entry(pos, struct block_node, node);
if (block && block->addr == addr && block->size == size) {
list_del(&block->node);
kfree(block);
}
}
}
}
#if IS_ENABLED(CONFIG_AMLOGIC_OPTEE)
dmabuf_manage_destroy_secmem_pool(pool);
#endif
}
mutex_unlock(&g_secure_pool_mutex);
return 0;
}
int dmabuf_manage_get_secure_heap_version(void)
{
return secure_heap_version;
}
static int dmabuf_manage_open(struct inode *inodep, struct file *filep)
{
pr_enter();
return 0;
}
static ssize_t dmabuf_manage_read(struct file *filep, char *buffer,
size_t len, loff_t *offset)
{
pr_enter();
return 0;
}
static ssize_t dmabuf_manage_write(struct file *filep, const char *buffer,
size_t len, loff_t *offset)
{
pr_enter();
return 0;
}
static int dmabuf_manage_release(struct inode *inodep, struct file *filep)
{
pr_enter();
return 0;
}
static long dmabuf_manage_ioctl(struct file *filep, unsigned int cmd,
unsigned long args)
{
long ret = -EINVAL;
pr_inf("cmd %x\n", cmd);
switch (cmd) {
case DMABUF_MANAGE_EXPORT_DMA:
ret = dmabuf_manage_export(args);
break;
case DMABUF_MANAGE_GET_HANDLE:
ret = dmabuf_manage_get_handle(args);
break;
case DMABUF_MANAGE_GET_PHYADDR:
ret = dmabuf_manage_get_phyaddr(args);
break;
case DMABUF_MANAGE_IMPORT_DMA:
ret = dmabuf_manage_import(args);
break;
case DMABUF_MANAGE_REGISTER_CHANNEL:
ret = dmabuf_manage_register_channel(args);
break;
case DMABUF_MANAGE_VERSION:
ret = AML_DMA_BUF_MANAGER_VERSION;
break;
case DMABUF_MANAGE_EXPORT_DMABUF:
ret = dmabuf_manage_export_dmabuf(args);
break;
case DMABUF_MANAGE_GET_DMABUFINFO:
ret = dmabuf_manage_get_dmabufinfo(args);
break;
case DMABUF_MANAGE_SET_FILTERFD:
ret = dmabuf_manage_set_filterfd(args);
break;
case DMABUF_MANAGE_ALLOCDMABUF:
ret = dmabuf_manage_alloc_dmabuf(args);
break;
default:
break;
}
return ret;
}
#ifdef CONFIG_COMPAT
static long dmabuf_manage_compat_ioctl(struct file *filep, unsigned int cmd,
unsigned long args)
{
unsigned long ret;
args = (unsigned long)compat_ptr(args);
ret = dmabuf_manage_ioctl(filep, cmd, args);
return ret;
}
#endif
const struct file_operations fops = {
.owner = THIS_MODULE,
.open = dmabuf_manage_open,
.read = dmabuf_manage_read,
.write = dmabuf_manage_write,
.release = dmabuf_manage_release,
.unlocked_ioctl = dmabuf_manage_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = dmabuf_manage_compat_ioctl,
#endif
};
static ssize_t dmabuf_manage_dump_show(const struct class *class,
const struct class_attribute *attr,
char *buf)
{
struct list_head *pos = NULL;
struct list_head *tmp = NULL;
struct secure_pool_info *pool = NULL;
mutex_lock(&g_secure_pool_mutex);
if (!list_empty(&pool_list) && dmabuf_manage_debug >= 6) {
list_for_each_safe(pos, tmp, &pool_list) {
pool = list_entry(pos, struct secure_pool_info, node);
dmabuf_manage_dump_secure_pool(pool);
}
}
mutex_unlock(&g_secure_pool_mutex);
return 0;
}
static ssize_t dmabuf_manage_config_show(const struct class *class,
const struct class_attribute *attr,
char *buf)
{
ssize_t ret;
ret = configs_list_path_nodes(CONFIG_PATH, buf, PAGE_SIZE,
LIST_MODE_NODE_CMDVAL_ALL);
return ret;
}
static ssize_t dmabuf_manage_config_store(const struct class *class,
const struct class_attribute *attr,
const char *buf, size_t size)
{
int ret;
ret = configs_set_prefix_path_valonpath(CONFIG_PREFIX, buf);
if (ret < 0)
pr_err("set config failed %s\n", buf);
return size;
}
static struct mconfig dmabuf_manage_configs[] = {
MC_PI32("secure_vdec_config", &secure_vdec_config),
MC_PI32("secure_heap_version", &secure_heap_version)
};
static CLASS_ATTR_RO(dmabuf_manage_dump);
static CLASS_ATTR_RW(dmabuf_manage_config);
static struct attribute *dmabuf_manage_class_attrs[] = {
&class_attr_dmabuf_manage_dump.attr,
&class_attr_dmabuf_manage_config.attr,
NULL
};
ATTRIBUTE_GROUPS(dmabuf_manage_class);
static struct class dmabuf_manage_class = {
.name = CLASS_NAME,
.class_groups = dmabuf_manage_class_groups,
};
int __init dmabuf_manage_init(void)
{
int ret;
ret = register_chrdev(0, DEVICE_NAME, &fops);
if (ret < 0) {
pr_error("register_chrdev failed\n");
goto error_register;
}
dev_no = ret;
ret = class_register(&dmabuf_manage_class);
if (ret < 0) {
pr_error("class_register failed\n");
goto error_class;
}
dmabuf_manage_dev = device_create(&dmabuf_manage_class,
NULL, MKDEV(dev_no, 0),
NULL, DEVICE_NAME);
if (IS_ERR(dmabuf_manage_dev)) {
pr_error("device_create failed\n");
ret = PTR_ERR(dmabuf_manage_dev);
goto error_create;
}
REG_PATH_CONFIGS(CONFIG_PATH, dmabuf_manage_configs);
INIT_LIST_HEAD(&dmx_filter_list);
INIT_LIST_HEAD(&pool_list);
pr_dbg("init done\n");
return 0;
error_create:
class_unregister(&dmabuf_manage_class);
error_class:
unregister_chrdev(dev_no, DEVICE_NAME);
error_register:
return ret;
}
void __exit dmabuf_manage_exit(void)
{
device_destroy(&dmabuf_manage_class, MKDEV(dev_no, 0));
class_unregister(&dmabuf_manage_class);
class_destroy(&dmabuf_manage_class);
unregister_chrdev(dev_no, DEVICE_NAME);
pr_dbg("exit done\n");
}
MODULE_LICENSE("GPL");