blob: e9664e9d3941fb74380a38aa96958c077106fe23 [file] [log] [blame]
/*
* drivers/amlogic/media/common/codec_mm/configs/configs_module.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <linux/amlogic/media/codec_mm/configs.h>
#include <linux/amlogic/media/codec_mm/configs_api.h>
#define MAX_OPENED_CNT 65536
#include "configs_priv.h"
#define MODULE_NAME "media-configs-dev"
static struct class *config_dev_class;
static unsigned int config_major;
struct mediaconfig_node {
const char *parent;
const char *name;
const char *dev_name;
int rw_flags;
atomic_t opened_cnt;
struct device *class_dev;
struct mconfig_node node;
};
static ssize_t all_show(struct class *class,
struct class_attribute *attr, char *buf)
{
ssize_t s;
s = configs_list_nodes(NULL, buf, PAGE_SIZE,
LIST_MODE_NODE_CMDVAL_ALL);
if (s > 0)
return s;
return -EPERM;
}
static ssize_t media_show(struct class *class,
struct class_attribute *attr, char *buf)
{
ssize_t s;
s = configs_list_path_nodes("media", buf, PAGE_SIZE,
LIST_MODE_NODE_CMDVAL_ALL);
if (s > 0)
return s;
return -EPERM;
}
static ssize_t video_show(struct class *class,
struct class_attribute *attr, char *buf)
{
ssize_t s;
s = configs_list_path_nodes("media.video", buf, PAGE_SIZE,
LIST_MODE_NODE_CMDVAL_ALL);
if (s > 0)
return s;
return -EPERM;
}
static ssize_t decoder_show(struct class *class,
struct class_attribute *attr, char *buf)
{
ssize_t s;
s = configs_list_path_nodes("media.decoder", buf, PAGE_SIZE,
LIST_MODE_NODE_CMDVAL_ALL);
if (s > 0)
return s;
return -EPERM;
}
static ssize_t vdec_show(struct class *class,
struct class_attribute *attr, char *buf)
{
ssize_t s;
s = configs_list_path_nodes("media.vdec", buf, PAGE_SIZE,
LIST_MODE_NODE_CMDVAL_ALL);
if (s > 0)
return s;
return -EPERM;
}
static ssize_t tsync_show(struct class *class,
struct class_attribute *attr, char *buf)
{
ssize_t s;
s = configs_list_path_nodes("media.tsync", buf, PAGE_SIZE,
LIST_MODE_NODE_CMDVAL_ALL);
if (s > 0)
return s;
return -EPERM;
}
static ssize_t amports_show(struct class *class,
struct class_attribute *attr, char *buf)
{
ssize_t s;
s = configs_list_path_nodes("media.amports", buf, PAGE_SIZE,
LIST_MODE_NODE_CMDVAL_ALL);
if (s > 0)
return s;
return -EPERM;
}
static ssize_t parser_show(struct class *class,
struct class_attribute *attr, char *buf)
{
ssize_t s;
s = configs_list_path_nodes("media.parser", buf, PAGE_SIZE,
LIST_MODE_NODE_CMDVAL_ALL);
if (s > 0)
return s;
return -EPERM;
}
static ssize_t show_config(struct class *class,
struct class_attribute *attr, char *buf)
{
ssize_t s;
s = configs_list_nodes(NULL, buf, PAGE_SIZE,
LIST_MODE_NODE_CMDVAL_ALL);
if (s > 0)
return s;
return -EPERM;
}
static ssize_t store_config(struct class *class,
struct class_attribute *attr,
const char *buf, size_t size)
{
ssize_t ret;
ret = configs_set_path_valonpath(buf);
if (ret >= 0)
return size;
return ret;
}
static ssize_t show_config_debug(struct class *class,
struct class_attribute *attr, char *buf)
{
return config_dump(buf, PAGE_SIZE);
}
static ssize_t store_config_debug(struct class *class,
struct class_attribute *attr,
const char *buf, size_t size)
{
configs_config_setstr(buf);
return size;
}
static ssize_t audio_show(struct class *class,
struct class_attribute *attr, char *buf)
{
ssize_t s;
s = configs_list_path_nodes("media.audio", buf, PAGE_SIZE,
LIST_MODE_NODE_CMDVAL_ALL);
if (s > 0)
return s;
return -EPERM;
}
static ssize_t vfm_show(struct class *class,
struct class_attribute *attr, char *buf)
{
ssize_t s;
s = configs_list_path_nodes("media.vfm", buf, PAGE_SIZE,
LIST_MODE_NODE_CMDVAL_ALL);
if (s > 0)
return s;
return -EPERM;
}
static struct class_attribute configs_class_attrs[] = {
__ATTR_RO(all),
__ATTR_RO(media),
__ATTR_RO(video),
__ATTR_RO(decoder),
__ATTR_RO(amports),
__ATTR_RO(tsync),
__ATTR_RO(parser),
__ATTR_RO(vdec),
__ATTR_RO(audio),
__ATTR_RO(vfm),
__ATTR(config, 0664,
show_config, store_config),
__ATTR(debug, 0664,
show_config_debug, store_config_debug),
__ATTR_NULL
};
static struct class media_configs_class = {
.name = "media-configs",
.class_attrs = configs_class_attrs,
};
static struct mediaconfig_node mediaconfig_nodes[] = {
{"", "media", "media", CONFIG_FOR_RW},
{"media", "decoder", "media.decoder", CONFIG_FOR_RW},
{"media", "parser", "media.parser", CONFIG_FOR_RW},
{"media", "video", "media.video", CONFIG_FOR_RW},
{"media", "amports", "media.amports", CONFIG_FOR_RW},
{"media", "tsync", "media.tsync", CONFIG_FOR_RW},
{"media", "codec_mm", "media.codec_mm", CONFIG_FOR_RW},
{"media", "audio", "media.audio", CONFIG_FOR_RW},
{"media", "vfm", "media.vfm", CONFIG_FOR_RW},
};
struct config_file_private {
struct mediaconfig_node *node;
char common[128];
pid_t pid;
pid_t tgid;
long opened_jiffies;
long last_access_jiffies;
long access_get_cnt;
long access_set_cnt;
long access_dump_cnt;
long error_cnt;
int enable_trace_get;
int enable_trace_set;
int last_read_end;
};
static int configs_open(struct inode *inode, struct file *file)
{
struct mediaconfig_node *node = &mediaconfig_nodes[iminor(inode)];
struct config_file_private *priv;
if (atomic_read(&node->opened_cnt) > MAX_OPENED_CNT) {
pr_err("too many files opened.!!\n");
return -EMFILE;
}
priv = kzalloc(sizeof(struct config_file_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
file->private_data = priv;
priv->pid = current->pid;
priv->tgid = current->tgid;
priv->opened_jiffies = jiffies;
priv->node = node;
atomic_inc(&node->opened_cnt);
configs_inc_node_ref(&node->node);
return 0;
}
static ssize_t configs_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
struct config_file_private *priv = file->private_data;
struct mediaconfig_node *node = priv->node;
int ret;
if (*ppos > 0)
return 0;/*don't support seek read. read end.*/
if (!access_ok(VERIFY_WRITE, buf, count))
return -EIO;
ret = configs_list_nodes(&node->node, buf, count,
LIST_MODE_NODE_CMDVAL_ALL);
if (ret > 0)
*ppos = ret;
return ret;
}
static int configs_close(struct inode *inode, struct file *file)
{
struct config_file_private *priv = file->private_data;
struct mediaconfig_node *node = priv->node;
configs_dec_node_ref(&node->node);
atomic_dec(&node->opened_cnt);
kfree(priv);
return 0;
}
static long configs_ioctl(struct file *file, unsigned int cmd, ulong arg)
{
struct config_file_private *priv = file->private_data;
struct mediaconfig_node *node = priv->node;
struct media_config_io_str io;
struct media_config_io_str *user_io = (void *)arg;
int r = -1;
priv->last_access_jiffies = jiffies;
switch (cmd) {
case MEDIA_CONFIG_SET_CMD_STR:
priv->access_set_cnt++;
r = copy_from_user(io.cmd_path,
user_io->cmd_path, sizeof(io.cmd_path));
r |= copy_from_user(io.val, user_io->val, sizeof(io.val));
/*pr_info("set%s:%s dev=%s\n", io.cmd_path,*/
/*io.val, node->dev_name);*/
if (r) {
r = -EIO;
break;
}
if (!strncmp(io.cmd_path, node->dev_name,
strlen(node->dev_name)))
r = configs_set_node_nodepath_str(NULL,
io.cmd_path,
io.val);
else
pr_info("set %s %s not match devname=%s\n",
io.cmd_path, io.val, node->dev_name);
break;
case MEDIA_CONFIG_GET_CMD_STR:
r = -1;
priv->access_get_cnt++;
r = copy_from_user(&io.cmd_path,
user_io->cmd_path, sizeof(io.cmd_path));
io.cmd_path[sizeof(io.cmd_path) - 1] = '\0';
if (r) {
r = -EIO;
break;
}
io.val[0] = '\0';
if (!strncmp(io.cmd_path, node->dev_name,
strlen(node->dev_name))) {
r = configs_get_node_nodepath_str(NULL,
io.cmd_path,
io.val,
sizeof(io.val));
/*pr_info("configs_get_node_nodepath_str*/
/*ret %x [%s]\n", r, io.val);*/
if (r > 0 && r <= sizeof(io.val) - 1) {
/*+1 for end str.*/
if (copy_to_user(user_io->val, io.val, r + 1) != 0) {
r = -EIO;
break;
}
put_user(r, &user_io->ret);
r = 0;
} else {
put_user(-1, &user_io->ret);
}
} else
pr_info("set %s %s not match devname=%s\n",
io.cmd_path, io.val, node->dev_name);
break;
default:
r = -EINVAL;
pr_err("unsupport cmd %x\n", cmd);
}
if (r < 0)
priv->error_cnt++;
return r;
}
#ifdef CONFIG_COMPAT
static long configs_compat_ioctl(struct file *file,
unsigned int cmd, ulong arg)
{
return configs_ioctl(file, cmd, (ulong)compat_ptr(arg));
}
#endif
static const struct file_operations configs_fops = {
.owner = THIS_MODULE,
.open = configs_open,
.read = configs_read,
.release = configs_close,
.unlocked_ioctl = configs_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = configs_compat_ioctl,
#endif
};
static int __init configs_init_devices(void)
{
int i;
int num = sizeof(mediaconfig_nodes)/sizeof(struct mediaconfig_node);
int r;
r = class_register(&media_configs_class);
if (r) {
pr_err("configs class create fail.\n");
goto error1;
}
r = register_chrdev(0, MODULE_NAME, &configs_fops);
if (r < 0) {
pr_err("Can't allocate major for config device\n");
goto error1;
}
config_major = r;
config_dev_class = class_create(THIS_MODULE, MODULE_NAME);
num = sizeof(mediaconfig_nodes)/sizeof(struct mediaconfig_node);
for (i = 0; i < num; i++) {
struct mediaconfig_node *mnode = &mediaconfig_nodes[i];
mnode->class_dev = device_create(config_dev_class, NULL,
MKDEV(config_major, i), NULL,
mnode->dev_name);
if (mnode->class_dev == NULL) {
pr_err("device_create %s failed\n", mnode->dev_name);
r = -1;
goto error2;
}
}
return 0;
#if 0
error3:
for (mnode = &mediaconfig_nodes[0], i = 0; i < num; i++, mnode++)
device_destroy(config_dev_class, MKDEV(config_major, i));
class_destroy(config_dev_class);
#endif
error2:
unregister_chrdev(config_major, MODULE_NAME);
error1:
class_destroy(&media_configs_class);
return r;
}
module_init(configs_init_devices);
static int __init media_configs_system_init(void)
{
int i;
int num = sizeof(mediaconfig_nodes)/sizeof(struct mediaconfig_node);
struct mediaconfig_node *mnode;
int r;
pr_info("media_configs_system_init\n");
configs_config_system_init();
for (i = 0; i < num; i++) {
mnode = &mediaconfig_nodes[i];
configs_init_new_node(&mnode->node, mnode->name,
mnode->rw_flags);
r = configs_register_path_node(mnode->parent,
&mnode->node);
if (r < 0) {
pr_err("ERR!!!.register node[%s] to [%s] failed!\n",
mnode->name, mnode->parent);
return r;
}
}
return 0;
}
arch_initcall(media_configs_system_init);
MODULE_DESCRIPTION("AMLOGIC config modules driver");
MODULE_LICENSE("GPL");