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