root/drivers/media/dvb-core/dvbdev.c
// SPDX-License-Identifier: LGPL-2.1-or-later
/*
 * dvbdev.c
 *
 * Copyright (C) 2000 Ralph  Metzler <ralph@convergence.de>
 *                  & Marcus Metzler <marcus@convergence.de>
 *                    for convergence integrated media GmbH
 */

#define pr_fmt(fmt) "dvbdev: " fmt

#include <linux/types.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/mutex.h>
#include <media/dvbdev.h>

/* Due to enum tuner_pad_index */
#include <media/tuner.h>

static DEFINE_MUTEX(dvbdev_mutex);
static LIST_HEAD(dvbdevfops_list);
static int dvbdev_debug;

module_param(dvbdev_debug, int, 0644);
MODULE_PARM_DESC(dvbdev_debug, "Turn on/off device debugging (default:off).");

#define dprintk(fmt, arg...) do {                                       \
        if (dvbdev_debug)                                               \
                printk(KERN_DEBUG pr_fmt("%s: " fmt),                   \
                       __func__, ##arg);                                \
} while (0)

static LIST_HEAD(dvb_adapter_list);
static DEFINE_MUTEX(dvbdev_register_lock);

static const char * const dnames[] = {
        [DVB_DEVICE_VIDEO] =            "video",
        [DVB_DEVICE_AUDIO] =            "audio",
        [DVB_DEVICE_SEC] =              "sec",
        [DVB_DEVICE_FRONTEND] =         "frontend",
        [DVB_DEVICE_DEMUX] =            "demux",
        [DVB_DEVICE_DVR] =              "dvr",
        [DVB_DEVICE_CA] =               "ca",
        [DVB_DEVICE_NET] =              "net",
        [DVB_DEVICE_OSD] =              "osd"
};

#ifdef CONFIG_DVB_DYNAMIC_MINORS
#define MAX_DVB_MINORS          256
#define DVB_MAX_IDS             MAX_DVB_MINORS
#else
#define DVB_MAX_IDS             4

static const u8 minor_type[] = {
        [DVB_DEVICE_VIDEO]      = 0,
        [DVB_DEVICE_AUDIO]      = 1,
        [DVB_DEVICE_SEC]        = 2,
        [DVB_DEVICE_FRONTEND]   = 3,
        [DVB_DEVICE_DEMUX]      = 4,
        [DVB_DEVICE_DVR]        = 5,
        [DVB_DEVICE_CA]         = 6,
        [DVB_DEVICE_NET]        = 7,
        [DVB_DEVICE_OSD]        = 8,
};

#define nums2minor(num, type, id) \
        (((num) << 6) | ((id) << 4) | minor_type[type])

#define MAX_DVB_MINORS          (DVB_MAX_ADAPTERS * 64)
#endif

static struct class *dvb_class;

static struct dvb_device *dvb_minors[MAX_DVB_MINORS];
static DECLARE_RWSEM(minor_rwsem);

static int dvb_device_open(struct inode *inode, struct file *file)
{
        struct dvb_device *dvbdev;
        unsigned int minor = iminor(inode);

        if (minor >= MAX_DVB_MINORS)
                return -ENODEV;

        mutex_lock(&dvbdev_mutex);
        down_read(&minor_rwsem);

        dvbdev = dvb_minors[minor];

        if (dvbdev && dvbdev->fops) {
                int err = 0;
                const struct file_operations *new_fops;

                new_fops = fops_get(dvbdev->fops);
                if (!new_fops)
                        goto fail;
                file->private_data = dvb_device_get(dvbdev);
                replace_fops(file, new_fops);
                if (file->f_op->open)
                        err = file->f_op->open(inode, file);
                up_read(&minor_rwsem);
                mutex_unlock(&dvbdev_mutex);
                if (err)
                        dvb_device_put(dvbdev);
                return err;
        }
fail:
        up_read(&minor_rwsem);
        mutex_unlock(&dvbdev_mutex);
        return -ENODEV;
}

static const struct file_operations dvb_device_fops = {
        .owner =        THIS_MODULE,
        .open =         dvb_device_open,
        .llseek =       noop_llseek,
};

static struct cdev dvb_device_cdev;

int dvb_generic_open(struct inode *inode, struct file *file)
{
        struct dvb_device *dvbdev = file->private_data;

        if (!dvbdev)
                return -ENODEV;

        if (!dvbdev->users)
                return -EBUSY;

        if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
                if (!dvbdev->readers)
                        return -EBUSY;
                dvbdev->readers--;
        } else {
                if (!dvbdev->writers)
                        return -EBUSY;
                dvbdev->writers--;
        }

        dvbdev->users--;
        return 0;
}
EXPORT_SYMBOL(dvb_generic_open);

int dvb_generic_release(struct inode *inode, struct file *file)
{
        struct dvb_device *dvbdev = file->private_data;

        if (!dvbdev)
                return -ENODEV;

        if ((file->f_flags & O_ACCMODE) == O_RDONLY)
                dvbdev->readers++;
        else
                dvbdev->writers++;

        dvbdev->users++;

        dvb_device_put(dvbdev);

        return 0;
}
EXPORT_SYMBOL(dvb_generic_release);

long dvb_generic_ioctl(struct file *file,
                       unsigned int cmd, unsigned long arg)
{
        struct dvb_device *dvbdev = file->private_data;

        if (!dvbdev)
                return -ENODEV;

        if (!dvbdev->kernel_ioctl)
                return -EINVAL;

        return dvb_usercopy(file, cmd, arg, dvbdev->kernel_ioctl);
}
EXPORT_SYMBOL(dvb_generic_ioctl);

static int dvbdev_get_free_id(struct dvb_adapter *adap, int type)
{
        u32 id = 0;

        while (id < DVB_MAX_IDS) {
                struct dvb_device *dev;

                list_for_each_entry(dev, &adap->device_list, list_head)
                        if (dev->type == type && dev->id == id)
                                goto skip;
                return id;
skip:
                id++;
        }
        return -ENFILE;
}

static void dvb_media_device_free(struct dvb_device *dvbdev)
{
#if defined(CONFIG_MEDIA_CONTROLLER_DVB)
        if (dvbdev->entity) {
                media_device_unregister_entity(dvbdev->entity);
                kfree(dvbdev->entity);
                kfree(dvbdev->pads);
                dvbdev->entity = NULL;
                dvbdev->pads = NULL;
        }

        if (dvbdev->tsout_entity) {
                int i;

                for (i = 0; i < dvbdev->tsout_num_entities; i++) {
                        media_device_unregister_entity(&dvbdev->tsout_entity[i]);
                        kfree(dvbdev->tsout_entity[i].name);
                }
                kfree(dvbdev->tsout_entity);
                kfree(dvbdev->tsout_pads);
                dvbdev->tsout_entity = NULL;
                dvbdev->tsout_pads = NULL;

                dvbdev->tsout_num_entities = 0;
        }

        if (dvbdev->intf_devnode) {
                media_devnode_remove(dvbdev->intf_devnode);
                dvbdev->intf_devnode = NULL;
        }

        if (dvbdev->adapter->conn) {
                media_device_unregister_entity(dvbdev->adapter->conn);
                kfree(dvbdev->adapter->conn);
                dvbdev->adapter->conn = NULL;
                kfree(dvbdev->adapter->conn_pads);
                dvbdev->adapter->conn_pads = NULL;
        }
#endif
}

#if defined(CONFIG_MEDIA_CONTROLLER_DVB)
static int dvb_create_tsout_entity(struct dvb_device *dvbdev,
                                   const char *name, int npads)
{
        int i;

        dvbdev->tsout_pads = kzalloc_objs(*dvbdev->tsout_pads, npads);
        if (!dvbdev->tsout_pads)
                return -ENOMEM;

        dvbdev->tsout_entity = kzalloc_objs(*dvbdev->tsout_entity, npads);
        if (!dvbdev->tsout_entity)
                return -ENOMEM;

        dvbdev->tsout_num_entities = npads;

        for (i = 0; i < npads; i++) {
                struct media_pad *pads = &dvbdev->tsout_pads[i];
                struct media_entity *entity = &dvbdev->tsout_entity[i];
                int ret;

                entity->name = kasprintf(GFP_KERNEL, "%s #%d", name, i);
                if (!entity->name)
                        return -ENOMEM;

                entity->function = MEDIA_ENT_F_IO_DTV;
                pads->flags = MEDIA_PAD_FL_SINK;

                ret = media_entity_pads_init(entity, 1, pads);
                if (ret < 0)
                        return ret;

                ret = media_device_register_entity(dvbdev->adapter->mdev,
                                                   entity);
                if (ret < 0)
                        return ret;
        }
        return 0;
}

#define DEMUX_TSOUT     "demux-tsout"
#define DVR_TSOUT       "dvr-tsout"

static int dvb_create_media_entity(struct dvb_device *dvbdev,
                                   int type, int demux_sink_pads)
{
        int i, ret, npads;

        switch (type) {
        case DVB_DEVICE_FRONTEND:
                npads = 2;
                break;
        case DVB_DEVICE_DVR:
                ret = dvb_create_tsout_entity(dvbdev, DVR_TSOUT,
                                              demux_sink_pads);
                return ret;
        case DVB_DEVICE_DEMUX:
                npads = 1 + demux_sink_pads;
                ret = dvb_create_tsout_entity(dvbdev, DEMUX_TSOUT,
                                              demux_sink_pads);
                if (ret < 0)
                        return ret;
                break;
        case DVB_DEVICE_CA:
                npads = 2;
                break;
        case DVB_DEVICE_NET:
                /*
                 * We should be creating entities for the MPE/ULE
                 * decapsulation hardware (or software implementation).
                 *
                 * However, the number of for the MPE/ULE decaps may not be
                 * fixed. As we don't have yet dynamic support for PADs at
                 * the Media Controller, let's not create the decap
                 * entities yet.
                 */
                return 0;
        default:
                return 0;
        }

        dvbdev->entity = kzalloc_obj(*dvbdev->entity);
        if (!dvbdev->entity)
                return -ENOMEM;

        dvbdev->entity->name = dvbdev->name;

        if (npads) {
                dvbdev->pads = kzalloc_objs(*dvbdev->pads, npads);
                if (!dvbdev->pads) {
                        kfree(dvbdev->entity);
                        dvbdev->entity = NULL;
                        return -ENOMEM;
                }
        }

        switch (type) {
        case DVB_DEVICE_FRONTEND:
                dvbdev->entity->function = MEDIA_ENT_F_DTV_DEMOD;
                dvbdev->pads[0].flags = MEDIA_PAD_FL_SINK;
                dvbdev->pads[1].flags = MEDIA_PAD_FL_SOURCE;
                break;
        case DVB_DEVICE_DEMUX:
                dvbdev->entity->function = MEDIA_ENT_F_TS_DEMUX;
                dvbdev->pads[0].flags = MEDIA_PAD_FL_SINK;
                for (i = 1; i < npads; i++)
                        dvbdev->pads[i].flags = MEDIA_PAD_FL_SOURCE;
                break;
        case DVB_DEVICE_CA:
                dvbdev->entity->function = MEDIA_ENT_F_DTV_CA;
                dvbdev->pads[0].flags = MEDIA_PAD_FL_SINK;
                dvbdev->pads[1].flags = MEDIA_PAD_FL_SOURCE;
                break;
        default:
                /* Should never happen, as the first switch prevents it */
                kfree(dvbdev->entity);
                kfree(dvbdev->pads);
                dvbdev->entity = NULL;
                dvbdev->pads = NULL;
                return 0;
        }

        if (npads) {
                ret = media_entity_pads_init(dvbdev->entity, npads, dvbdev->pads);
                if (ret)
                        return ret;
        }
        ret = media_device_register_entity(dvbdev->adapter->mdev,
                                           dvbdev->entity);
        if (ret)
                return ret;

        pr_info("%s: media entity '%s' registered.\n",
                __func__, dvbdev->entity->name);

        return 0;
}
#endif

static int dvb_register_media_device(struct dvb_device *dvbdev,
                                     int type, int minor,
                                     unsigned int demux_sink_pads)
{
#if defined(CONFIG_MEDIA_CONTROLLER_DVB)
        struct media_link *link;
        u32 intf_type;
        int ret;

        if (!dvbdev->adapter->mdev)
                return 0;

        ret = dvb_create_media_entity(dvbdev, type, demux_sink_pads);
        if (ret)
                return ret;

        switch (type) {
        case DVB_DEVICE_FRONTEND:
                intf_type = MEDIA_INTF_T_DVB_FE;
                break;
        case DVB_DEVICE_DEMUX:
                intf_type = MEDIA_INTF_T_DVB_DEMUX;
                break;
        case DVB_DEVICE_DVR:
                intf_type = MEDIA_INTF_T_DVB_DVR;
                break;
        case DVB_DEVICE_CA:
                intf_type = MEDIA_INTF_T_DVB_CA;
                break;
        case DVB_DEVICE_NET:
                intf_type = MEDIA_INTF_T_DVB_NET;
                break;
        default:
                return 0;
        }

        dvbdev->intf_devnode = media_devnode_create(dvbdev->adapter->mdev,
                                                    intf_type, 0,
                                                    DVB_MAJOR, minor);

        if (!dvbdev->intf_devnode)
                return -ENOMEM;

        /*
         * Create the "obvious" link, e. g. the ones that represent
         * a direct association between an interface and an entity.
         * Other links should be created elsewhere, like:
         *              DVB FE intf    -> tuner
         *              DVB demux intf -> dvr
         */

        if (!dvbdev->entity)
                return 0;

        link = media_create_intf_link(dvbdev->entity,
                                      &dvbdev->intf_devnode->intf,
                                      MEDIA_LNK_FL_ENABLED |
                                      MEDIA_LNK_FL_IMMUTABLE);
        if (!link)
                return -ENOMEM;
#endif
        return 0;
}

int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
                        const struct dvb_device *template, void *priv,
                        enum dvb_device_type type, int demux_sink_pads)
{
        struct dvb_device *dvbdev;
        struct file_operations *dvbdevfops = NULL;
        struct dvbdevfops_node *node = NULL, *new_node = NULL;
        struct device *clsdev;
        int minor;
        int id, ret;

        mutex_lock(&dvbdev_register_lock);

        id = dvbdev_get_free_id(adap, type);
        if (id < 0) {
                mutex_unlock(&dvbdev_register_lock);
                *pdvbdev = NULL;
                pr_err("%s: couldn't find free device id\n", __func__);
                return -ENFILE;
        }

        *pdvbdev = dvbdev = kzalloc_obj(*dvbdev);
        if (!dvbdev) {
                mutex_unlock(&dvbdev_register_lock);
                return -ENOMEM;
        }

        /*
         * When a device of the same type is probe()d more than once,
         * the first allocated fops are used. This prevents memory leaks
         * that can occur when the same device is probe()d repeatedly.
         */
        list_for_each_entry(node, &dvbdevfops_list, list_head) {
                if (node->fops->owner == adap->module &&
                    node->type == type && node->template == template) {
                        dvbdevfops = node->fops;
                        break;
                }
        }

        if (!dvbdevfops) {
                dvbdevfops = kmemdup(template->fops, sizeof(*dvbdevfops), GFP_KERNEL);
                if (!dvbdevfops) {
                        kfree(dvbdev);
                        *pdvbdev = NULL;
                        mutex_unlock(&dvbdev_register_lock);
                        return -ENOMEM;
                }

                new_node = kzalloc_obj(*new_node);
                if (!new_node) {
                        kfree(dvbdevfops);
                        kfree(dvbdev);
                        *pdvbdev = NULL;
                        mutex_unlock(&dvbdev_register_lock);
                        return -ENOMEM;
                }

                new_node->fops = dvbdevfops;
                new_node->type = type;
                new_node->template = template;
                list_add_tail(&new_node->list_head, &dvbdevfops_list);
        }

        memcpy(dvbdev, template, sizeof(struct dvb_device));
        kref_init(&dvbdev->ref);
        dvbdev->type = type;
        dvbdev->id = id;
        dvbdev->adapter = adap;
        dvbdev->priv = priv;
        dvbdev->fops = dvbdevfops;
        init_waitqueue_head(&dvbdev->wait_queue);
        dvbdevfops->owner = adap->module;
        list_add_tail(&dvbdev->list_head, &adap->device_list);
        down_write(&minor_rwsem);
#ifdef CONFIG_DVB_DYNAMIC_MINORS
        for (minor = 0; minor < MAX_DVB_MINORS; minor++)
                if (!dvb_minors[minor])
                        break;
#else
        minor = nums2minor(adap->num, type, id);
#endif
        if (minor >= MAX_DVB_MINORS) {
                if (new_node) {
                        list_del(&new_node->list_head);
                        kfree(dvbdevfops);
                        kfree(new_node);
                }
                list_del(&dvbdev->list_head);
                kfree(dvbdev);
                *pdvbdev = NULL;
                up_write(&minor_rwsem);
                mutex_unlock(&dvbdev_register_lock);
                return -EINVAL;
        }

        dvbdev->minor = minor;
        dvb_minors[minor] = dvb_device_get(dvbdev);
        up_write(&minor_rwsem);
        ret = dvb_register_media_device(dvbdev, type, minor, demux_sink_pads);
        if (ret) {
                pr_err("%s: dvb_register_media_device failed to create the mediagraph\n",
                       __func__);
                if (new_node) {
                        list_del(&new_node->list_head);
                        kfree(dvbdevfops);
                        kfree(new_node);
                }
                dvb_media_device_free(dvbdev);
                list_del(&dvbdev->list_head);
                kfree(dvbdev);
                *pdvbdev = NULL;
                mutex_unlock(&dvbdev_register_lock);
                return ret;
        }

        clsdev = device_create(dvb_class, adap->device,
                               MKDEV(DVB_MAJOR, minor),
                               dvbdev, "dvb%d.%s%d", adap->num, dnames[type], id);
        if (IS_ERR(clsdev)) {
                pr_err("%s: failed to create device dvb%d.%s%d (%pe)\n",
                       __func__, adap->num, dnames[type], id, clsdev);
                if (new_node) {
                        list_del(&new_node->list_head);
                        kfree(dvbdevfops);
                        kfree(new_node);
                }
                dvb_media_device_free(dvbdev);
                list_del(&dvbdev->list_head);
                kfree(dvbdev);
                *pdvbdev = NULL;
                mutex_unlock(&dvbdev_register_lock);
                return PTR_ERR(clsdev);
        }

        dprintk("DVB: register adapter%d/%s%d @ minor: %i (0x%02x)\n",
                adap->num, dnames[type], id, minor, minor);

        mutex_unlock(&dvbdev_register_lock);
        return 0;
}
EXPORT_SYMBOL(dvb_register_device);

void dvb_remove_device(struct dvb_device *dvbdev)
{
        if (!dvbdev)
                return;

        down_write(&minor_rwsem);
        dvb_minors[dvbdev->minor] = NULL;
        dvb_device_put(dvbdev);
        up_write(&minor_rwsem);

        dvb_media_device_free(dvbdev);

        device_destroy(dvb_class, MKDEV(DVB_MAJOR, dvbdev->minor));

        list_del(&dvbdev->list_head);
}
EXPORT_SYMBOL(dvb_remove_device);

static void dvb_free_device(struct kref *ref)
{
        struct dvb_device *dvbdev = container_of(ref, struct dvb_device, ref);

        kfree(dvbdev);
}

struct dvb_device *dvb_device_get(struct dvb_device *dvbdev)
{
        kref_get(&dvbdev->ref);
        return dvbdev;
}
EXPORT_SYMBOL(dvb_device_get);

void dvb_device_put(struct dvb_device *dvbdev)
{
        if (dvbdev)
                kref_put(&dvbdev->ref, dvb_free_device);
}

void dvb_unregister_device(struct dvb_device *dvbdev)
{
        dvb_remove_device(dvbdev);
        dvb_device_put(dvbdev);
}
EXPORT_SYMBOL(dvb_unregister_device);

#ifdef CONFIG_MEDIA_CONTROLLER_DVB

static int dvb_create_io_intf_links(struct dvb_adapter *adap,
                                    struct media_interface *intf,
                                    char *name)
{
        struct media_device *mdev = adap->mdev;
        struct media_entity *entity;
        struct media_link *link;

        media_device_for_each_entity(entity, mdev) {
                if (entity->function == MEDIA_ENT_F_IO_DTV) {
                        if (strncmp(entity->name, name, strlen(name)))
                                continue;
                        link = media_create_intf_link(entity, intf,
                                                      MEDIA_LNK_FL_ENABLED |
                                                      MEDIA_LNK_FL_IMMUTABLE);
                        if (!link)
                                return -ENOMEM;
                }
        }
        return 0;
}

int dvb_create_media_graph(struct dvb_adapter *adap,
                           bool create_rf_connector)
{
        struct media_device *mdev = adap->mdev;
        struct media_entity *entity, *tuner = NULL, *demod = NULL, *conn;
        struct media_entity *demux = NULL, *ca = NULL;
        struct media_link *link;
        struct media_interface *intf;
        unsigned int demux_pad = 0;
        unsigned int dvr_pad = 0;
        unsigned int ntuner = 0, ndemod = 0;
        int ret, pad_source, pad_sink;
        static const char *connector_name = "Television";

        if (!mdev)
                return 0;

        media_device_for_each_entity(entity, mdev) {
                switch (entity->function) {
                case MEDIA_ENT_F_TUNER:
                        tuner = entity;
                        ntuner++;
                        break;
                case MEDIA_ENT_F_DTV_DEMOD:
                        demod = entity;
                        ndemod++;
                        break;
                case MEDIA_ENT_F_TS_DEMUX:
                        demux = entity;
                        break;
                case MEDIA_ENT_F_DTV_CA:
                        ca = entity;
                        break;
                }
        }

        /*
         * Prepare to signalize to media_create_pad_links() that multiple
         * entities of the same type exists and a 1:n or n:1 links need to be
         * created.
         * NOTE: if both tuner and demod have multiple instances, it is up
         * to the caller driver to create such links.
         */
        if (ntuner > 1)
                tuner = NULL;
        if (ndemod > 1)
                demod = NULL;

        if (create_rf_connector) {
                conn = kzalloc_obj(*conn);
                if (!conn)
                        return -ENOMEM;
                adap->conn = conn;

                adap->conn_pads = kzalloc_obj(*adap->conn_pads);
                if (!adap->conn_pads)
                        return -ENOMEM;

                conn->flags = MEDIA_ENT_FL_CONNECTOR;
                conn->function = MEDIA_ENT_F_CONN_RF;
                conn->name = connector_name;
                adap->conn_pads->flags = MEDIA_PAD_FL_SOURCE;

                ret = media_entity_pads_init(conn, 1, adap->conn_pads);
                if (ret)
                        return ret;

                ret = media_device_register_entity(mdev, conn);
                if (ret)
                        return ret;

                if (!ntuner) {
                        ret = media_create_pad_links(mdev,
                                                     MEDIA_ENT_F_CONN_RF,
                                                     conn, 0,
                                                     MEDIA_ENT_F_DTV_DEMOD,
                                                     demod, 0,
                                                     MEDIA_LNK_FL_ENABLED,
                                                     false);
                } else {
                        pad_sink = media_get_pad_index(tuner, MEDIA_PAD_FL_SINK,
                                                       PAD_SIGNAL_ANALOG);
                        if (pad_sink < 0)
                                return -EINVAL;
                        ret = media_create_pad_links(mdev,
                                                     MEDIA_ENT_F_CONN_RF,
                                                     conn, 0,
                                                     MEDIA_ENT_F_TUNER,
                                                     tuner, pad_sink,
                                                     MEDIA_LNK_FL_ENABLED,
                                                     false);
                }
                if (ret)
                        return ret;
        }

        if (ntuner && ndemod) {
                /* NOTE: first found tuner source pad presumed correct */
                pad_source = media_get_pad_index(tuner, MEDIA_PAD_FL_SOURCE,
                                                 PAD_SIGNAL_ANALOG);
                if (pad_source < 0)
                        return -EINVAL;
                ret = media_create_pad_links(mdev,
                                             MEDIA_ENT_F_TUNER,
                                             tuner, pad_source,
                                             MEDIA_ENT_F_DTV_DEMOD,
                                             demod, 0, MEDIA_LNK_FL_ENABLED,
                                             false);
                if (ret)
                        return ret;
        }

        if (ndemod && demux) {
                ret = media_create_pad_links(mdev,
                                             MEDIA_ENT_F_DTV_DEMOD,
                                             demod, 1,
                                             MEDIA_ENT_F_TS_DEMUX,
                                             demux, 0, MEDIA_LNK_FL_ENABLED,
                                             false);
                if (ret)
                        return ret;
        }
        if (demux && ca) {
                ret = media_create_pad_link(demux, 1, ca,
                                            0, MEDIA_LNK_FL_ENABLED);
                if (ret)
                        return ret;
        }

        /* Create demux links for each ringbuffer/pad */
        if (demux) {
                media_device_for_each_entity(entity, mdev) {
                        if (entity->function == MEDIA_ENT_F_IO_DTV) {
                                if (!strncmp(entity->name, DVR_TSOUT,
                                             strlen(DVR_TSOUT))) {
                                        ret = media_create_pad_link(demux,
                                                                    ++dvr_pad,
                                                                    entity, 0, 0);
                                        if (ret)
                                                return ret;
                                }
                                if (!strncmp(entity->name, DEMUX_TSOUT,
                                             strlen(DEMUX_TSOUT))) {
                                        ret = media_create_pad_link(demux,
                                                                    ++demux_pad,
                                                                    entity, 0, 0);
                                        if (ret)
                                                return ret;
                                }
                        }
                }
        }

        /* Create interface links for FE->tuner, DVR->demux and CA->ca */
        media_device_for_each_intf(intf, mdev) {
                if (intf->type == MEDIA_INTF_T_DVB_CA && ca) {
                        link = media_create_intf_link(ca, intf,
                                                      MEDIA_LNK_FL_ENABLED |
                                                      MEDIA_LNK_FL_IMMUTABLE);
                        if (!link)
                                return -ENOMEM;
                }

                if (intf->type == MEDIA_INTF_T_DVB_FE && tuner) {
                        link = media_create_intf_link(tuner, intf,
                                                      MEDIA_LNK_FL_ENABLED |
                                                      MEDIA_LNK_FL_IMMUTABLE);
                        if (!link)
                                return -ENOMEM;
                }
#if 0
                /*
                 * Indirect link - let's not create yet, as we don't know how
                 *                 to handle indirect links, nor if this will
                 *                 actually be needed.
                 */
                if (intf->type == MEDIA_INTF_T_DVB_DVR && demux) {
                        link = media_create_intf_link(demux, intf,
                                                      MEDIA_LNK_FL_ENABLED |
                                                      MEDIA_LNK_FL_IMMUTABLE);
                        if (!link)
                                return -ENOMEM;
                }
#endif
                if (intf->type == MEDIA_INTF_T_DVB_DVR) {
                        ret = dvb_create_io_intf_links(adap, intf, DVR_TSOUT);
                        if (ret)
                                return ret;
                }
                if (intf->type == MEDIA_INTF_T_DVB_DEMUX) {
                        ret = dvb_create_io_intf_links(adap, intf, DEMUX_TSOUT);
                        if (ret)
                                return ret;
                }
        }
        return 0;
}
EXPORT_SYMBOL_GPL(dvb_create_media_graph);
#endif

static int dvbdev_check_free_adapter_num(int num)
{
        struct list_head *entry;

        list_for_each(entry, &dvb_adapter_list) {
                struct dvb_adapter *adap;

                adap = list_entry(entry, struct dvb_adapter, list_head);
                if (adap->num == num)
                        return 0;
        }
        return 1;
}

static int dvbdev_get_free_adapter_num(void)
{
        int num = 0;

        while (num < DVB_MAX_ADAPTERS) {
                if (dvbdev_check_free_adapter_num(num))
                        return num;
                num++;
        }

        return -ENFILE;
}

int dvb_register_adapter(struct dvb_adapter *adap, const char *name,
                         struct module *module, struct device *device,
                         short *adapter_nums)
{
        int i, num;

        mutex_lock(&dvbdev_register_lock);

        for (i = 0; i < DVB_MAX_ADAPTERS; ++i) {
                num = adapter_nums[i];
                if (num >= 0  &&  num < DVB_MAX_ADAPTERS) {
                /* use the one the driver asked for */
                        if (dvbdev_check_free_adapter_num(num))
                                break;
                } else {
                        num = dvbdev_get_free_adapter_num();
                        break;
                }
                num = -1;
        }

        if (num < 0) {
                mutex_unlock(&dvbdev_register_lock);
                return -ENFILE;
        }

        memset(adap, 0, sizeof(struct dvb_adapter));
        INIT_LIST_HEAD(&adap->device_list);

        pr_info("DVB: registering new adapter (%s)\n", name);

        adap->num = num;
        adap->name = name;
        adap->module = module;
        adap->device = device;
        adap->mfe_shared = 0;
        adap->mfe_dvbdev = NULL;
        mutex_init(&adap->mfe_lock);

#ifdef CONFIG_MEDIA_CONTROLLER_DVB
        mutex_init(&adap->mdev_lock);
#endif

        list_add_tail(&adap->list_head, &dvb_adapter_list);

        mutex_unlock(&dvbdev_register_lock);

        return num;
}
EXPORT_SYMBOL(dvb_register_adapter);

int dvb_unregister_adapter(struct dvb_adapter *adap)
{
        mutex_lock(&dvbdev_register_lock);
        list_del(&adap->list_head);
        mutex_unlock(&dvbdev_register_lock);
        return 0;
}
EXPORT_SYMBOL(dvb_unregister_adapter);

/*
 * if the miracle happens and "generic_usercopy()" is included into
 * the kernel, then this can vanish. please don't make the mistake and
 * define this as video_usercopy(). this will introduce a dependency
 * to the v4l "videodev.o" module, which is unnecessary for some
 * cards (ie. the budget dvb-cards don't need the v4l module...)
 */
int dvb_usercopy(struct file *file,
                 unsigned int cmd, unsigned long arg,
                 int (*func)(struct file *file,
                             unsigned int cmd, void *arg))
{
        char    sbuf[128] = {};
        void    *mbuf = NULL;
        void    *parg = NULL;
        int     err  = -EINVAL;

        /*  Copy arguments into temp kernel buffer  */
        switch (_IOC_DIR(cmd)) {
        case _IOC_NONE:
                /*
                 * For this command, the pointer is actually an integer
                 * argument.
                 */
                parg = (void *)arg;
                break;
        case _IOC_READ: /* some v4l ioctls are marked wrong ... */
        case _IOC_WRITE:
        case (_IOC_WRITE | _IOC_READ):
                if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {
                        parg = sbuf;
                } else {
                        /* too big to allocate from stack */
                        mbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL);
                        if (!mbuf)
                                return -ENOMEM;
                        parg = mbuf;
                }

                err = -EFAULT;
                if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd)))
                        goto out;
                break;
        }

        /* call driver */
        err = func(file, cmd, parg);
        if (err == -ENOIOCTLCMD)
                err = -ENOTTY;

        if (err < 0)
                goto out;

        /*  Copy results into user buffer  */
        switch (_IOC_DIR(cmd)) {
        case _IOC_READ:
        case (_IOC_WRITE | _IOC_READ):
                if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd)))
                        err = -EFAULT;
                break;
        }

out:
        kfree(mbuf);
        return err;
}

#if IS_ENABLED(CONFIG_I2C)
struct i2c_client *dvb_module_probe(const char *module_name,
                                    const char *name,
                                    struct i2c_adapter *adap,
                                    unsigned char addr,
                                    void *platform_data)
{
        struct i2c_client *client;
        struct i2c_board_info *board_info;

        board_info = kzalloc_obj(*board_info);
        if (!board_info)
                return NULL;

        if (name)
                strscpy(board_info->type, name, I2C_NAME_SIZE);
        else
                strscpy(board_info->type, module_name, I2C_NAME_SIZE);

        board_info->addr = addr;
        board_info->platform_data = platform_data;
        request_module(module_name);
        client = i2c_new_client_device(adap, board_info);
        if (!i2c_client_has_driver(client)) {
                kfree(board_info);
                return NULL;
        }

        if (!try_module_get(client->dev.driver->owner)) {
                i2c_unregister_device(client);
                client = NULL;
        }

        kfree(board_info);
        return client;
}
EXPORT_SYMBOL_GPL(dvb_module_probe);

void dvb_module_release(struct i2c_client *client)
{
        if (!client)
                return;

        module_put(client->dev.driver->owner);
        i2c_unregister_device(client);
}
EXPORT_SYMBOL_GPL(dvb_module_release);
#endif

static int dvb_uevent(const struct device *dev, struct kobj_uevent_env *env)
{
        const struct dvb_device *dvbdev = dev_get_drvdata(dev);

        add_uevent_var(env, "DVB_ADAPTER_NUM=%d", dvbdev->adapter->num);
        add_uevent_var(env, "DVB_DEVICE_TYPE=%s", dnames[dvbdev->type]);
        add_uevent_var(env, "DVB_DEVICE_NUM=%d", dvbdev->id);
        return 0;
}

static char *dvb_devnode(const struct device *dev, umode_t *mode)
{
        const struct dvb_device *dvbdev = dev_get_drvdata(dev);

        return kasprintf(GFP_KERNEL, "dvb/adapter%d/%s%d",
                dvbdev->adapter->num, dnames[dvbdev->type], dvbdev->id);
}

static int __init init_dvbdev(void)
{
        int retval;
        dev_t dev = MKDEV(DVB_MAJOR, 0);

        retval = register_chrdev_region(dev, MAX_DVB_MINORS, "DVB");
        if (retval != 0) {
                pr_err("dvb-core: unable to get major %d\n", DVB_MAJOR);
                return retval;
        }

        cdev_init(&dvb_device_cdev, &dvb_device_fops);
        retval = cdev_add(&dvb_device_cdev, dev, MAX_DVB_MINORS);
        if (retval != 0) {
                pr_err("dvb-core: unable register character device\n");
                goto error;
        }

        dvb_class = class_create("dvb");
        if (IS_ERR(dvb_class)) {
                retval = PTR_ERR(dvb_class);
                goto error;
        }
        dvb_class->dev_uevent = dvb_uevent;
        dvb_class->devnode = dvb_devnode;
        return 0;

error:
        cdev_del(&dvb_device_cdev);
        unregister_chrdev_region(dev, MAX_DVB_MINORS);
        return retval;
}

static void __exit exit_dvbdev(void)
{
        struct dvbdevfops_node *node, *next;

        class_destroy(dvb_class);
        cdev_del(&dvb_device_cdev);
        unregister_chrdev_region(MKDEV(DVB_MAJOR, 0), MAX_DVB_MINORS);

        list_for_each_entry_safe(node, next, &dvbdevfops_list, list_head) {
                list_del(&node->list_head);
                kfree(node->fops);
                kfree(node);
        }
}

subsys_initcall(init_dvbdev);
module_exit(exit_dvbdev);

MODULE_DESCRIPTION("DVB Core Driver");
MODULE_AUTHOR("Marcus Metzler, Ralph Metzler, Holger Waechtler");
MODULE_LICENSE("GPL");