Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[media] media: Media device node support

The media_devnode structure provides support for registering and
unregistering character devices using a dynamic major number. Reference
counting is handled internally, making device drivers easier to write
without having to solve the open/disconnect race condition issue over
and over again.

The code is based on video/v4l2-dev.c.

[mchehab@redhat.com: Remove linux/smp_lock.h include to not break compilation on bisect]
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>

authored by

Laurent Pinchart and committed by
Mauro Carvalho Chehab
cf4b9211 02adb1cc

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