You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
442 lines
10 KiB
442 lines
10 KiB
/*
|
|
* Copyright (c) Hisilicon Technologies Co., Ltd. 2008-2021. All rights reserved.
|
|
* Description: Generic misc open routine.
|
|
* Author: Hisilicon
|
|
* Create: 2008-01-01
|
|
*/
|
|
#include <linux/module.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/major.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/stat.h>
|
|
#include <linux/init.h>
|
|
#include <linux/device.h>
|
|
#include <linux/tty.h>
|
|
#include <linux/kmod.h>
|
|
#include <linux/delay.h>
|
|
|
|
#include "drv_media_ext.h"
|
|
#include "media_base.h"
|
|
|
|
#define mkstr(exp) #exp
|
|
#define mkmarcotostr(exp) mkstr(exp)
|
|
#define VERSION_STRING ("SDK_VERSION:[" mkmarcotostr(SDK_VERSION) "] Build Time:[" __DATE__ ", " __TIME__ "]")
|
|
|
|
/*
|
|
* Head entry for the doubly linked media_device list
|
|
*/
|
|
static LIST_HEAD(g_media_list);
|
|
static DEFINE_MUTEX(g_media_sem);
|
|
|
|
/*
|
|
* Assigned numbers, used for dynamic minors
|
|
*/
|
|
#define DYNAMIC_MINORS 128 /* like dynamic majors */
|
|
static unsigned char g_media_minors[DYNAMIC_MINORS / 8]; /* 8 表示以8为粒度 */
|
|
|
|
#ifdef CONFIG_PROC_FS
|
|
static void *media_seq_start(struct seq_file *seq, loff_t *pos)
|
|
{
|
|
mutex_lock(&g_media_sem);
|
|
return seq_list_start(&g_media_list, *pos);
|
|
}
|
|
|
|
static void *media_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
|
{
|
|
return seq_list_next(v, &g_media_list, pos);
|
|
}
|
|
|
|
static void media_seq_stop(struct seq_file *seq, void *v)
|
|
{
|
|
mutex_unlock(&g_media_sem);
|
|
}
|
|
|
|
static int media_seq_show(struct seq_file *seq, void *v)
|
|
{
|
|
const pm_device *p = list_entry(v, pm_device, list);
|
|
|
|
seq_printf(seq, "%3i %s\n", p->minor, (char *)p->name ? (char *)p->name : "");
|
|
return 0;
|
|
}
|
|
|
|
static struct seq_operations g_media_seq_ops = {
|
|
.start = media_seq_start,
|
|
.next = media_seq_next,
|
|
.stop = media_seq_stop,
|
|
.show = media_seq_show,
|
|
};
|
|
|
|
static int media_seq_open(struct inode *inode, struct file *file)
|
|
{
|
|
return seq_open(file, &g_media_seq_ops);
|
|
}
|
|
|
|
#if (LINUX_VERSION_CODE >=KERNEL_VERSION(5, 10, 0))
|
|
static struct proc_ops g_media_proc_fops = {
|
|
.proc_open = media_seq_open,
|
|
.proc_read = seq_read,
|
|
.proc_lseek = seq_lseek,
|
|
.proc_release = seq_release,
|
|
};
|
|
#else
|
|
static struct file_operations g_media_proc_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = media_seq_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = seq_release,
|
|
};
|
|
#endif
|
|
#endif
|
|
|
|
static int media_open(struct inode *inode, struct file *file)
|
|
{
|
|
td_u32 minor = iminor(inode);
|
|
pm_device *c = NULL;
|
|
int err = -1;
|
|
const struct file_operations *old_fops = NULL;
|
|
const struct file_operations *new_fops = NULL;
|
|
|
|
mutex_lock(&g_media_sem);
|
|
|
|
list_for_each_entry(c, &g_media_list, list)
|
|
{
|
|
if (c->minor == minor) {
|
|
new_fops = fops_get(c->app_ops);
|
|
break;
|
|
}
|
|
}
|
|
mutex_unlock(&g_media_sem);
|
|
|
|
if (new_fops == NULL) {
|
|
goto fail;
|
|
}
|
|
|
|
err = 0;
|
|
old_fops = file->f_op;
|
|
file->f_op = new_fops;
|
|
if (file->f_op->open) {
|
|
file->private_data = c->dev;
|
|
err = file->f_op->open(inode, file);
|
|
if (err) {
|
|
if (file->f_op != NULL && file->f_op->owner != NULL) {
|
|
fops_put(file->f_op);
|
|
}
|
|
if (old_fops != NULL && old_fops->owner != NULL) {
|
|
file->f_op = fops_get(old_fops);
|
|
}
|
|
}
|
|
}
|
|
if (old_fops != NULL && old_fops->owner != NULL) {
|
|
fops_put(old_fops);
|
|
}
|
|
fail:
|
|
return err;
|
|
}
|
|
|
|
static struct class *g_media_class;
|
|
|
|
static struct file_operations g_media_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = media_open,
|
|
};
|
|
|
|
/**
|
|
* ext_drv_pm_register - register a media device
|
|
* @media: device structure
|
|
*
|
|
* Register a media device with the kernel. If the minor
|
|
* number is set to %EXT_DRV_MEDIA_DYNAMIC_MINOR a minor number is assigned
|
|
* and placed in the minor field of the structure. For other cases
|
|
* the minor number requested is used.
|
|
*
|
|
* The structure passed is linked into the kernel and may not be
|
|
* destroyed until it has been unregistered.
|
|
*
|
|
* A zero is returned on success and a negative errno code for
|
|
* failure.
|
|
*/
|
|
static int pm_register_get_media_minor(pm_device *media, pm_device **pdev)
|
|
{
|
|
pm_device *c = NULL;
|
|
int i = DYNAMIC_MINORS;
|
|
|
|
if (media == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
list_for_each_entry(c, &g_media_list, list) {
|
|
if (c->minor == media->minor) {
|
|
return -EBUSY;
|
|
}
|
|
|
|
if (c->minor == media->parent_minor) {
|
|
*pdev = c;
|
|
}
|
|
}
|
|
if (media->minor == EXT_DRV_MEDIA_DYNAMIC_MINOR) {
|
|
while (--i >= 0) {
|
|
if (((td_u32)i >> 0x3) >= (DYNAMIC_MINORS / 8)) { /* 8 表示以8为粒度 */
|
|
continue;
|
|
}
|
|
if ((g_media_minors[(td_u32)i >> 0x3] & (1 << ((td_u32)i & 0x7))) == 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i < 0) {
|
|
return -EBUSY;
|
|
}
|
|
media->minor = (td_u32)i;
|
|
}
|
|
|
|
if (media->minor < DYNAMIC_MINORS) {
|
|
g_media_minors[media->minor >> 0x3] |= 1 << (media->minor & 0x7);
|
|
}
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static int pm_register_add_media_device(const pm_device *media, pm_basedev **bdev, pm_device *pdev)
|
|
{
|
|
int ret;
|
|
|
|
if (bdev == NULL) {
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
*bdev = media_device_alloc(media->name, -1);
|
|
if (*bdev == NULL) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
if (pdev != NULL) {
|
|
(*bdev)->dev.parent = pdev->app_device;
|
|
pr_info("PM_RESUME:devices:%s parent minor:%d", media->name, media->parent_minor);
|
|
}
|
|
|
|
ret = media_device_add(*bdev);
|
|
if (ret) {
|
|
media_device_put(*bdev);
|
|
return ret;
|
|
}
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static int pm_register_device_create(pm_device *media,
|
|
pm_basedev *bdev, dev_t *dev, struct device **adev)
|
|
{
|
|
int ret;
|
|
|
|
*dev = MKDEV(EXT_DRV_MEDIA_DEVICE_MAJOR, media->minor);
|
|
*adev = device_create(g_media_class, &(bdev->dev), *dev, NULL,
|
|
"%s", media->name);
|
|
if (IS_ERR(*adev)) {
|
|
ret = PTR_ERR(*adev);
|
|
return ret;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static int pm_register_device_register(pm_device *media, struct device *adev, pm_basedev *bdev)
|
|
{
|
|
pm_basedrv *bdrv = NULL;
|
|
int ret;
|
|
|
|
bdrv = media_driver_alloc(media->name, media->owner, media->base_ops);
|
|
if (bdrv == NULL) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
ret = media_driver_register(bdrv);
|
|
if (ret) {
|
|
media_driver_release(bdrv);
|
|
return ret;
|
|
}
|
|
|
|
media->app_device = adev;
|
|
media->base_device = bdev;
|
|
media->base_driver = bdrv;
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
int drv_pm_register(pm_device *media)
|
|
{
|
|
int ret;
|
|
dev_t dev;
|
|
struct device *adev = NULL;
|
|
pm_basedev *bdev = NULL;
|
|
pm_device *pdev = NULL;
|
|
|
|
if ((media == NULL) || (media->name == NULL) || (media->app_ops == NULL)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
#ifdef MODULE
|
|
if (media->owner == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
#endif
|
|
mutex_lock(&g_media_sem);
|
|
/* 1 */
|
|
ret = pm_register_get_media_minor(media, &pdev);
|
|
if (ret != TD_SUCCESS) {
|
|
goto out;
|
|
}
|
|
|
|
/* 2 base device, then class is NULL */
|
|
ret = pm_register_add_media_device(media, &bdev, pdev);
|
|
if (ret != TD_SUCCESS) {
|
|
goto err0;
|
|
}
|
|
|
|
/* 3 app class */
|
|
ret = pm_register_device_create(media, bdev, &dev, &adev);
|
|
if (ret != TD_SUCCESS) {
|
|
goto err1;
|
|
}
|
|
|
|
/* 4 base driver */
|
|
ret = pm_register_device_register(media, adev, bdev);
|
|
if (ret != TD_SUCCESS) {
|
|
goto err2;
|
|
}
|
|
/*
|
|
* Add it to the front, so that later devices can "override"
|
|
* earlier defaults
|
|
*/
|
|
INIT_LIST_HEAD(&media->list);
|
|
list_add(&media->list, &g_media_list);
|
|
goto out;
|
|
|
|
err2:
|
|
device_destroy(g_media_class, dev);
|
|
err1:
|
|
ext_media_device_unregister(bdev);
|
|
err0:
|
|
g_media_minors[media->minor >> 3] &= ~(1 << (media->minor & 7)); /* 3 1 7 表示å移ä½æ° */
|
|
out:
|
|
mutex_unlock(&g_media_sem);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* drv_pm_un_register - unregister a media device
|
|
* @media: device to unregister
|
|
*
|
|
* Unregister a media device that was previously
|
|
* successfully registered with ext_drv_pm_register(). Success
|
|
* is indicated by a zero return, a negative errno code
|
|
* indicates an error.
|
|
*/
|
|
int drv_pm_un_register(pm_device *media)
|
|
{
|
|
unsigned int i;
|
|
|
|
if (g_media_class == NULL || media == NULL || (media->name == NULL) || (media->app_ops == NULL)) {
|
|
return -EINVAL;
|
|
}
|
|
#ifdef MODULE
|
|
if (media->owner == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
#endif
|
|
i = media->minor;
|
|
if (list_empty(&media->list)) {
|
|
return -EINVAL;
|
|
}
|
|
if ((i >= DYNAMIC_MINORS) || (i < 0)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
mutex_lock(&g_media_sem);
|
|
list_del(&media->list);
|
|
|
|
if (media->base_driver) {
|
|
media_driver_unregister(media->base_driver);
|
|
media_driver_release(media->base_driver);
|
|
media->base_driver = NULL;
|
|
}
|
|
|
|
if (media->app_device) {
|
|
device_destroy(g_media_class, MKDEV(EXT_DRV_MEDIA_DEVICE_MAJOR, media->minor));
|
|
media->app_device = NULL;
|
|
}
|
|
|
|
if (media->base_device) {
|
|
ext_media_device_unregister(media->base_device);
|
|
media->base_device = NULL;
|
|
}
|
|
|
|
g_media_minors[i >> 3] &= ~(1 << (i & 7)); /* 3 1 7 表示å移ä½æ° */
|
|
mutex_unlock(&g_media_sem);
|
|
return 0;
|
|
}
|
|
|
|
int drv_pm_mod_init(void)
|
|
{
|
|
int ret;
|
|
|
|
proc_create("media", 0, NULL, &g_media_proc_fops);
|
|
|
|
ret = media_bus_init();
|
|
if (ret) {
|
|
goto err0;
|
|
}
|
|
|
|
g_media_class = class_create(THIS_MODULE, "media_class");
|
|
ret = PTR_ERR(g_media_class);
|
|
if (IS_ERR(g_media_class)) {
|
|
goto err1;
|
|
}
|
|
|
|
ret = -EIO;
|
|
if (register_chrdev(EXT_DRV_MEDIA_DEVICE_MAJOR, "media_char_dev", &g_media_fops)) {
|
|
goto err2;
|
|
}
|
|
#if defined(MODULE)
|
|
printk("Load soc_media.ko success.\t(%s)\n", VERSION_STRING);
|
|
#endif
|
|
return 0;
|
|
|
|
err2:
|
|
|
|
printk("!!! Module media: unable to get major %d for media devices\n", EXT_DRV_MEDIA_DEVICE_MAJOR);
|
|
|
|
class_destroy(g_media_class);
|
|
err1:
|
|
media_bus_exit();
|
|
err0:
|
|
remove_proc_entry("media", NULL);
|
|
return ret;
|
|
}
|
|
|
|
void drv_pm_mod_exit(void)
|
|
{
|
|
if (list_empty(&g_media_list) == 0) {
|
|
return;
|
|
}
|
|
|
|
unregister_chrdev(EXT_DRV_MEDIA_DEVICE_MAJOR, "media");
|
|
class_destroy(g_media_class);
|
|
media_bus_exit();
|
|
remove_proc_entry("media", NULL);
|
|
#if defined(MODULE)
|
|
printk("remove soc_media.ko success.\n");
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
EXPORT_SYMBOL(drv_pm_register);
|
|
EXPORT_SYMBOL(drv_pm_un_register);
|
|
EXPORT_SYMBOL(drv_pm_mod_init);
|
|
EXPORT_SYMBOL(drv_pm_mod_exit);
|
|
|
|
MODULE_LICENSE("GPL");
|