linux/drivers/media/media-devnode.c
<<
>>
Prefs
   1/*
   2 * Media device node
   3 *
   4 * Copyright (C) 2010 Nokia Corporation
   5 *
   6 * Based on drivers/media/video/v4l2_dev.c code authored by
   7 *      Mauro Carvalho Chehab <mchehab@infradead.org> (version 2)
   8 *      Alan Cox, <alan@lxorguk.ukuu.org.uk> (version 1)
   9 *
  10 * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
  11 *           Sakari Ailus <sakari.ailus@iki.fi>
  12 *
  13 * This program is free software; you can redistribute it and/or modify
  14 * it under the terms of the GNU General Public License version 2 as
  15 * published by the Free Software Foundation.
  16 *
  17 * This program is distributed in the hope that it will be useful,
  18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20 * GNU General Public License for more details.
  21 *
  22 * You should have received a copy of the GNU General Public License
  23 * along with this program; if not, write to the Free Software
  24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  25 *
  26 * --
  27 *
  28 * Generic media device node infrastructure to register and unregister
  29 * character devices using a dynamic major number and proper reference
  30 * counting.
  31 */
  32
  33#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  34
  35#include <linux/errno.h>
  36#include <linux/init.h>
  37#include <linux/module.h>
  38#include <linux/kernel.h>
  39#include <linux/kmod.h>
  40#include <linux/slab.h>
  41#include <linux/mm.h>
  42#include <linux/string.h>
  43#include <linux/types.h>
  44#include <linux/uaccess.h>
  45
  46#include <media/media-devnode.h>
  47
  48#define MEDIA_NUM_DEVICES       256
  49#define MEDIA_NAME              "media"
  50
  51static dev_t media_dev_t;
  52
  53/*
  54 *      Active devices
  55 */
  56static DEFINE_MUTEX(media_devnode_lock);
  57static DECLARE_BITMAP(media_devnode_nums, MEDIA_NUM_DEVICES);
  58
  59/* Called when the last user of the media device exits. */
  60static void media_devnode_release(struct device *cd)
  61{
  62        struct media_devnode *mdev = to_media_devnode(cd);
  63
  64        mutex_lock(&media_devnode_lock);
  65
  66        /* Delete the cdev on this minor as well */
  67        cdev_del(&mdev->cdev);
  68
  69        /* Mark device node number as free */
  70        clear_bit(mdev->minor, media_devnode_nums);
  71
  72        mutex_unlock(&media_devnode_lock);
  73
  74        /* Release media_devnode and perform other cleanups as needed. */
  75        if (mdev->release)
  76                mdev->release(mdev);
  77}
  78
  79static struct bus_type media_bus_type = {
  80        .name = MEDIA_NAME,
  81};
  82
  83static ssize_t media_read(struct file *filp, char __user *buf,
  84                size_t sz, loff_t *off)
  85{
  86        struct media_devnode *mdev = media_devnode_data(filp);
  87
  88        if (!mdev->fops->read)
  89                return -EINVAL;
  90        if (!media_devnode_is_registered(mdev))
  91                return -EIO;
  92        return mdev->fops->read(filp, buf, sz, off);
  93}
  94
  95static ssize_t media_write(struct file *filp, const char __user *buf,
  96                size_t sz, loff_t *off)
  97{
  98        struct media_devnode *mdev = media_devnode_data(filp);
  99
 100        if (!mdev->fops->write)
 101                return -EINVAL;
 102        if (!media_devnode_is_registered(mdev))
 103                return -EIO;
 104        return mdev->fops->write(filp, buf, sz, off);
 105}
 106
 107static unsigned int media_poll(struct file *filp,
 108                               struct poll_table_struct *poll)
 109{
 110        struct media_devnode *mdev = media_devnode_data(filp);
 111
 112        if (!media_devnode_is_registered(mdev))
 113                return POLLERR | POLLHUP;
 114        if (!mdev->fops->poll)
 115                return DEFAULT_POLLMASK;
 116        return mdev->fops->poll(filp, poll);
 117}
 118
 119static long media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 120{
 121        struct media_devnode *mdev = media_devnode_data(filp);
 122
 123        if (!mdev->fops->ioctl)
 124                return -ENOTTY;
 125
 126        if (!media_devnode_is_registered(mdev))
 127                return -EIO;
 128
 129        return mdev->fops->ioctl(filp, cmd, arg);
 130}
 131
 132/* Override for the open function */
 133static int media_open(struct inode *inode, struct file *filp)
 134{
 135        struct media_devnode *mdev;
 136        int ret;
 137
 138        /* Check if the media device is available. This needs to be done with
 139         * the media_devnode_lock held to prevent an open/unregister race:
 140         * without the lock, the device could be unregistered and freed between
 141         * the media_devnode_is_registered() and get_device() calls, leading to
 142         * a crash.
 143         */
 144        mutex_lock(&media_devnode_lock);
 145        mdev = container_of(inode->i_cdev, struct media_devnode, cdev);
 146        /* return ENXIO if the media device has been removed
 147           already or if it is not registered anymore. */
 148        if (!media_devnode_is_registered(mdev)) {
 149                mutex_unlock(&media_devnode_lock);
 150                return -ENXIO;
 151        }
 152        /* and increase the device refcount */
 153        get_device(&mdev->dev);
 154        mutex_unlock(&media_devnode_lock);
 155
 156        filp->private_data = mdev;
 157
 158        if (mdev->fops->open) {
 159                ret = mdev->fops->open(filp);
 160                if (ret) {
 161                        put_device(&mdev->dev);
 162                        return ret;
 163                }
 164        }
 165
 166        return 0;
 167}
 168
 169/* Override for the release function */
 170static int media_release(struct inode *inode, struct file *filp)
 171{
 172        struct media_devnode *mdev = media_devnode_data(filp);
 173        int ret = 0;
 174
 175        if (mdev->fops->release)
 176                mdev->fops->release(filp);
 177
 178        /* decrease the refcount unconditionally since the release()
 179           return value is ignored. */
 180        put_device(&mdev->dev);
 181        filp->private_data = NULL;
 182        return ret;
 183}
 184
 185static const struct file_operations media_devnode_fops = {
 186        .owner = THIS_MODULE,
 187        .read = media_read,
 188        .write = media_write,
 189        .open = media_open,
 190        .unlocked_ioctl = media_ioctl,
 191        .release = media_release,
 192        .poll = media_poll,
 193        .llseek = no_llseek,
 194};
 195
 196/**
 197 * media_devnode_register - register a media device node
 198 * @mdev: media device node structure we want to register
 199 *
 200 * The registration code assigns minor numbers and registers the new device node
 201 * with the kernel. An error is returned if no free minor number can be found,
 202 * or if the registration of the device node fails.
 203 *
 204 * Zero is returned on success.
 205 *
 206 * Note that if the media_devnode_register call fails, the release() callback of
 207 * the media_devnode structure is *not* called, so the caller is responsible for
 208 * freeing any data.
 209 */
 210int __must_check media_devnode_register(struct media_devnode *mdev)
 211{
 212        int minor;
 213        int ret;
 214
 215        /* Part 1: Find a free minor number */
 216        mutex_lock(&media_devnode_lock);
 217        minor = find_next_zero_bit(media_devnode_nums, MEDIA_NUM_DEVICES, 0);
 218        if (minor == MEDIA_NUM_DEVICES) {
 219                mutex_unlock(&media_devnode_lock);
 220                pr_err("could not get a free minor\n");
 221                return -ENFILE;
 222        }
 223
 224        set_bit(minor, media_devnode_nums);
 225        mutex_unlock(&media_devnode_lock);
 226
 227        mdev->minor = minor;
 228
 229        /* Part 2: Initialize and register the character device */
 230        cdev_init(&mdev->cdev, &media_devnode_fops);
 231        mdev->cdev.owner = mdev->fops->owner;
 232
 233        ret = cdev_add(&mdev->cdev, MKDEV(MAJOR(media_dev_t), mdev->minor), 1);
 234        if (ret < 0) {
 235                pr_err("%s: cdev_add failed\n", __func__);
 236                goto error;
 237        }
 238
 239        /* Part 3: Register the media device */
 240        mdev->dev.bus = &media_bus_type;
 241        mdev->dev.devt = MKDEV(MAJOR(media_dev_t), mdev->minor);
 242        mdev->dev.release = media_devnode_release;
 243        if (mdev->parent)
 244                mdev->dev.parent = mdev->parent;
 245        dev_set_name(&mdev->dev, "media%d", mdev->minor);
 246        ret = device_register(&mdev->dev);
 247        if (ret < 0) {
 248                pr_err("%s: device_register failed\n", __func__);
 249                goto error;
 250        }
 251
 252        /* Part 4: Activate this minor. The char device can now be used. */
 253        set_bit(MEDIA_FLAG_REGISTERED, &mdev->flags);
 254
 255        return 0;
 256
 257error:
 258        cdev_del(&mdev->cdev);
 259        clear_bit(mdev->minor, media_devnode_nums);
 260        return ret;
 261}
 262
 263/**
 264 * media_devnode_unregister - unregister a media device node
 265 * @mdev: the device node to unregister
 266 *
 267 * This unregisters the passed device. Future open calls will be met with
 268 * errors.
 269 *
 270 * This function can safely be called if the device node has never been
 271 * registered or has already been unregistered.
 272 */
 273void media_devnode_unregister(struct media_devnode *mdev)
 274{
 275        /* Check if mdev was ever registered at all */
 276        if (!media_devnode_is_registered(mdev))
 277                return;
 278
 279        mutex_lock(&media_devnode_lock);
 280        clear_bit(MEDIA_FLAG_REGISTERED, &mdev->flags);
 281        mutex_unlock(&media_devnode_lock);
 282        device_unregister(&mdev->dev);
 283}
 284
 285/*
 286 *      Initialise media for linux
 287 */
 288static int __init media_devnode_init(void)
 289{
 290        int ret;
 291
 292        pr_info("Linux media interface: v0.10\n");
 293        ret = alloc_chrdev_region(&media_dev_t, 0, MEDIA_NUM_DEVICES,
 294                                  MEDIA_NAME);
 295        if (ret < 0) {
 296                pr_warn("unable to allocate major\n");
 297                return ret;
 298        }
 299
 300        ret = bus_register(&media_bus_type);
 301        if (ret < 0) {
 302                unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES);
 303                pr_warn("bus_register failed\n");
 304                return -EIO;
 305        }
 306
 307        return 0;
 308}
 309
 310static void __exit media_devnode_exit(void)
 311{
 312        bus_unregister(&media_bus_type);
 313        unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES);
 314}
 315
 316subsys_initcall(media_devnode_init);
 317module_exit(media_devnode_exit)
 318
 319MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
 320MODULE_DESCRIPTION("Device node registration for media drivers");
 321MODULE_LICENSE("GPL");
 322
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.